[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH RFC 04/13] xen: implement basic PIRQ support for Dom0



This allows Dom0 to manage physical hardware, redirecting the
physical interrupts to event channels.
---
 sys/x86/xen/xen_intr.c |  190 +++++++++++++++++++++++++++++++++++++++++++++--
 sys/xen/xen_intr.h     |   11 +++
 2 files changed, 192 insertions(+), 9 deletions(-)

diff --git a/sys/x86/xen/xen_intr.c b/sys/x86/xen/xen_intr.c
index bc0781e..340e5ed 100644
--- a/sys/x86/xen/xen_intr.c
+++ b/sys/x86/xen/xen_intr.c
@@ -104,6 +104,8 @@ DPCPU_DECLARE(struct vcpu_info *, vcpu_info);
 
 #define is_valid_evtchn(x)     ((x) != 0)
 
+#define        EEXIST  17      /* Xen "already exists" error */
+
 struct xenisrc {
        struct intsrc   xi_intsrc;
        enum evtchn_type xi_type;
@@ -111,6 +113,9 @@ struct xenisrc {
        int             xi_vector;      /* Global isrc vector number. */
        evtchn_port_t   xi_port;
        int             xi_pirq;
+       int             xi_activehi:1;
+       int             xi_edgetrigger:1;
+       int             xi_configured:1;
        int             xi_virq;
        u_int           xi_close:1;     /* close on unbind? */
        u_int           xi_needs_eoi:1;
@@ -136,6 +141,9 @@ static void xen_intr_pirq_enable_source(struct intsrc 
*isrc);
 static void    xen_intr_pirq_disable_source(struct intsrc *isrc, int eoi);
 static void    xen_intr_pirq_eoi_source(struct intsrc *isrc);
 static void    xen_intr_pirq_enable_intr(struct intsrc *isrc);
+static void    xen_intr_pirq_disable_intr(struct intsrc *isrc);
+static int     xen_intr_pirq_config_intr(struct intsrc *isrc,
+                    enum intr_trigger trig, enum intr_polarity pol);
 
 /**
  * PIC interface for all event channel port types except physical IRQs.
@@ -163,12 +171,12 @@ struct pic xen_intr_pirq_pic = {
        .pic_disable_source = xen_intr_pirq_disable_source,
        .pic_eoi_source     = xen_intr_pirq_eoi_source,
        .pic_enable_intr    = xen_intr_pirq_enable_intr,
-       .pic_disable_intr   = xen_intr_disable_intr,
+       .pic_disable_intr   = xen_intr_pirq_disable_intr,
        .pic_vector         = xen_intr_vector,
        .pic_source_pending = xen_intr_source_pending,
        .pic_suspend        = xen_intr_suspend,
        .pic_resume         = xen_intr_resume,
-       .pic_config_intr    = xen_intr_config_intr,
+       .pic_config_intr    = xen_intr_pirq_config_intr,
        .pic_assign_cpu     = xen_intr_assign_cpu
 };
 
@@ -282,11 +290,10 @@ xen_intr_find_unused_isrc(enum evtchn_type type)
  *          object or NULL.
  */
 static struct xenisrc *
-xen_intr_alloc_isrc(enum evtchn_type type)
+xen_intr_alloc_isrc(enum evtchn_type type, int vector)
 {
        static int warned;
        struct xenisrc *isrc;
-       int vector;
 
        KASSERT(mtx_owned(&xen_intr_isrc_lock), ("Evtchn alloc lock not held"));
 
@@ -297,12 +304,19 @@ xen_intr_alloc_isrc(enum evtchn_type type)
                }
                return (NULL);
        }
-       vector = FIRST_EVTCHN_INT + xen_intr_isrc_count;
-       xen_intr_isrc_count++;
+
+       if (type != EVTCHN_TYPE_PIRQ) {
+               vector = FIRST_EVTCHN_INT + xen_intr_isrc_count;
+               xen_intr_isrc_count++;
+       }
+
+       KASSERT((intr_lookup_source(vector) == NULL),
+               ("Trying to use an already allocated vector"));
 
        mtx_unlock(&xen_intr_isrc_lock);
        isrc = malloc(sizeof(*isrc), M_XENINTR, M_WAITOK | M_ZERO);
-       isrc->xi_intsrc.is_pic = &xen_intr_pic;
+       isrc->xi_intsrc.is_pic = (type == EVTCHN_TYPE_PIRQ) ?
+                                 &xen_intr_pirq_pic : &xen_intr_pic;
        isrc->xi_vector = vector;
        isrc->xi_type = type;
        intr_register_source(&isrc->xi_intsrc);
@@ -388,7 +402,7 @@ xen_intr_bind_isrc(struct xenisrc **isrcp, evtchn_port_t 
local_port,
        mtx_lock(&xen_intr_isrc_lock);
        isrc = xen_intr_find_unused_isrc(type);
        if (isrc == NULL) {
-               isrc = xen_intr_alloc_isrc(type);
+               isrc = xen_intr_alloc_isrc(type, 0);
                if (isrc == NULL) {
                        mtx_unlock(&xen_intr_isrc_lock);
                        return (ENOSPC);
@@ -592,6 +606,10 @@ xen_intr_init(void *dummy __unused)
        }
 
        intr_register_pic(&xen_intr_pic);
+       intr_register_pic(&xen_intr_pirq_pic);
+
+       if (bootverbose)
+               printf("Xen interrupt system initialized\n");
 
        return (0);
 }
@@ -925,6 +943,9 @@ xen_intr_pirq_disable_source(struct intsrc *base_isrc, int 
eoi)
 
        isrc = (struct xenisrc *)base_isrc;
        evtchn_mask_port(isrc->xi_port);
+
+       if (eoi == PIC_EOI)
+               xen_intr_pirq_eoi_source(base_isrc);
 }
 
 /*
@@ -966,8 +987,115 @@ xen_intr_pirq_eoi_source(struct intsrc *base_isrc)
  * \param isrc  The interrupt source to enable.
  */
 static void
-xen_intr_pirq_enable_intr(struct intsrc *isrc)
+xen_intr_pirq_enable_intr(struct intsrc *base_isrc)
+{
+       struct xenisrc *isrc;
+       struct evtchn_bind_pirq bind_pirq;
+       struct physdev_irq_status_query irq_status;
+       int error;
+
+       isrc = (struct xenisrc *)base_isrc;
+
+       if (!isrc->xi_configured) {
+               xen_intr_pirq_config_intr(base_isrc,
+                                isrc->xi_edgetrigger ? INTR_TRIGGER_EDGE :
+                                                       INTR_TRIGGER_LEVEL,
+                                isrc->xi_activehi ? INTR_POLARITY_HIGH :
+                                                    INTR_POLARITY_LOW);
+       }
+
+       irq_status.irq = isrc->xi_pirq;
+       error = HYPERVISOR_physdev_op(PHYSDEVOP_irq_status_query, &irq_status);
+       if (error)
+               panic("unable to get status of IRQ#%d", isrc->xi_pirq);
+
+       if (irq_status.flags & XENIRQSTAT_needs_eoi)
+               isrc->xi_needs_eoi = 1;
+
+       bind_pirq.pirq = isrc->xi_pirq;
+       bind_pirq.flags = isrc->xi_edgetrigger ? 0 : BIND_PIRQ__WILL_SHARE;
+       error = HYPERVISOR_event_channel_op(EVTCHNOP_bind_pirq, &bind_pirq);
+       if (error)
+               panic("unable to bind IRQ#%d", isrc->xi_pirq);
+
+       isrc->xi_port = bind_pirq.port;
+
+       mtx_lock(&xen_intr_isrc_lock);
+       KASSERT((xen_intr_port_to_isrc[bind_pirq.port] == NULL),
+               ("trying to override an already setup event channel port"));
+       xen_intr_port_to_isrc[bind_pirq.port] = isrc;
+       mtx_unlock(&xen_intr_isrc_lock);
+
+       evtchn_unmask_port(isrc->xi_port);
+}
+
+/*
+ * Disable an interrupt source.
+ *
+ * \param isrc  The interrupt source to disable.
+ */
+static void
+xen_intr_pirq_disable_intr(struct intsrc *base_isrc)
+{
+       struct xenisrc *isrc;
+       struct evtchn_close close;
+       int error;
+
+       isrc = (struct xenisrc *)base_isrc;
+
+       close.port = isrc->xi_port;
+       error = HYPERVISOR_event_channel_op(EVTCHNOP_close, &close);
+       if (error)
+               panic("unable to close event channel %d IRQ#%d",
+                     isrc->xi_port, isrc->xi_pirq);
+
+       mtx_lock(&xen_intr_isrc_lock);
+       xen_intr_port_to_isrc[isrc->xi_port] = NULL;
+       mtx_unlock(&xen_intr_isrc_lock);
+
+       isrc->xi_port = 0;
+}
+
+/**
+ * Perform configuration of an interrupt source.
+ *
+ * \param isrc  The interrupt source to configure.
+ * \param trig  Edge or level.
+ * \param pol   Active high or low.
+ *
+ * \returns  0 if no events are pending, otherwise non-zero.
+ */
+static int
+xen_intr_pirq_config_intr(struct intsrc *base_isrc, enum intr_trigger trig,
+                          enum intr_polarity pol)
 {
+       struct xenisrc *isrc = (struct xenisrc *)base_isrc;
+       struct physdev_setup_gsi setup_gsi;
+       int error;
+
+       KASSERT(!(trig == INTR_TRIGGER_CONFORM || pol == INTR_POLARITY_CONFORM),
+           ("%s: Conforming trigger or polarity\n", __func__));
+
+       setup_gsi.gsi = isrc->xi_pirq;
+       setup_gsi.triggering = trig == INTR_TRIGGER_EDGE ? 0 : 1;
+       setup_gsi.polarity = pol == INTR_POLARITY_HIGH ? 0 : 1;
+
+       error = HYPERVISOR_physdev_op(PHYSDEVOP_setup_gsi, &setup_gsi);
+       if (error == -EEXIST) {
+               if ((isrc->xi_edgetrigger && (trig != INTR_TRIGGER_EDGE)) ||
+                   (isrc->xi_activehi && (pol != INTR_POLARITY_HIGH)))
+                       panic("unable to reconfigure interrupt IRQ#%d",
+                             isrc->xi_pirq);
+               error = 0;
+       }
+       if (error)
+               panic("unable to configure IRQ#%d\n", isrc->xi_pirq);
+
+       isrc->xi_configured = 1;
+       isrc->xi_activehi = pol == INTR_POLARITY_HIGH ? 1 : 0;
+       isrc->xi_edgetrigger = trig == INTR_POLARITY_HIGH ? 1 : 0;
+
+       return (0);
 }
 
 /*--------------------------- Public Functions 
-------------------------------*/
@@ -1190,6 +1318,50 @@ xen_intr_alloc_and_bind_ipi(device_t dev, u_int cpu,
 }
 
 int
+xen_register_pirq(int vector, int activehi, int edgetrigger)
+{
+       struct physdev_map_pirq map_pirq;
+       struct physdev_irq alloc_pirq;
+       struct xenisrc *isrc;
+       int error;
+
+       if (vector == 0)
+               return (EINVAL);
+
+       if (bootverbose)
+               printf("xen: register IRQ#%d\n", vector);
+
+       map_pirq.domid = DOMID_SELF;
+       map_pirq.type = MAP_PIRQ_TYPE_GSI;
+       map_pirq.index = vector;
+       map_pirq.pirq = vector;
+
+       error = HYPERVISOR_physdev_op(PHYSDEVOP_map_pirq, &map_pirq);
+       if (error) {
+               printf("xen: unable to map IRQ#%d\n", vector);
+               return (error);
+       }
+
+       alloc_pirq.irq = vector;
+       alloc_pirq.vector = 0;
+       error = HYPERVISOR_physdev_op(PHYSDEVOP_alloc_irq_vector, &alloc_pirq);
+       if (error) {
+               printf("xen: unable to alloc PIRQ for IRQ#%d\n", vector);
+               return (error);
+       }
+
+       mtx_lock(&xen_intr_isrc_lock);
+       isrc = xen_intr_alloc_isrc(EVTCHN_TYPE_PIRQ, vector);
+       mtx_unlock(&xen_intr_isrc_lock);
+       KASSERT((isrc != NULL), ("xen: unable to allocate isrc for interrupt"));
+       isrc->xi_pirq = vector;
+       isrc->xi_activehi = activehi;
+       isrc->xi_edgetrigger = edgetrigger;
+
+       return (0);
+}
+
+int
 xen_intr_describe(xen_intr_handle_t port_handle, const char *fmt, ...)
 {
        char descr[MAXCOMLEN + 1];
diff --git a/sys/xen/xen_intr.h b/sys/xen/xen_intr.h
index 3b339a5..eda5fdf 100644
--- a/sys/xen/xen_intr.h
+++ b/sys/xen/xen_intr.h
@@ -159,6 +159,17 @@ int xen_intr_alloc_and_bind_ipi(device_t dev, u_int cpu,
        xen_intr_handle_t *handlep);
 
 /**
+ * Register a physical interrupt vector and setup the interrupt source.
+ *
+ * \param vector        The global vector to use.
+ * \param activehi      Default polarity of the interrupt.
+ * \param edgetrigger   Default trigger method.
+ *
+ * \returns  0 on success, otherwise an errno.
+ */
+int xen_register_pirq(int vector, int activehi, int edgetrigger);
+
+/**
  * Unbind an interrupt handler from its interrupt source.
  *
  * \param handlep  A pointer to the opaque handle that was initialized
-- 
1.7.7.5 (Apple Git-26)


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.