Date: Mon, 4 Aug 2014 08:42:29 +0000 (UTC) From: Roger Pau Monné <royger@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r269507 - in head/sys: x86/xen xen Message-ID: <53df4775.58a4.dae26d3@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: royger Date: Mon Aug 4 08:42:29 2014 New Revision: 269507 URL: http://svnweb.freebsd.org/changeset/base/269507 Log: xen: implement event channel PIRQ support This allows Dom0 to manage physical hardware, redirecting the physical interrupts to event channels. Sponsored by: Citrix Systems R&D x86/xen/xen_intr.c: - Expand struct xenisrc to hold the level and triggering of PIRQ event channels. - Implement missing methods in xen_intr_pirq_pic. - Allow xen_intr_alloc_isrc to take a vector parameter that globally identifies the interrupt. This is only used for PIRQs that are bound to a specific hardware IRQ. - Introduce xen_register_pirq used to register IO APIC legacy PIRQ interrupts. - Add support for the dynamic PIRQ EOI map, this shared memory is modified by Xen (if it suppoorts that feature), and notifies the guest if an EOI is needed or not. If it's not available fall back to the old implementation using PHYSDEVOP_irq_status_query. - Rename xen_intr_isrc_count to xen_intr_auto_vector_count and replace it's usages. - Align static variables by name. xen/xen_intr.h: - Add prototype for xen_register_pirq. Modified: head/sys/x86/xen/xen_intr.c head/sys/xen/xen_intr.h Modified: head/sys/x86/xen/xen_intr.c ============================================================================== --- head/sys/x86/xen/xen_intr.c Mon Aug 4 05:50:21 2014 (r269506) +++ head/sys/x86/xen/xen_intr.c Mon Aug 4 08:42:29 2014 (r269507) @@ -104,6 +104,9 @@ DPCPU_DECLARE(struct vcpu_info *, vcpu_i #define is_valid_evtchn(x) ((x) != 0) +#define XEN_EEXIST 17 /* Xen "already exists" error */ +#define XEN_ALLOCATE_VECTOR 0 /* Allocate a vector for this event channel */ + struct xenisrc { struct intsrc xi_intsrc; enum evtchn_type xi_type; @@ -113,8 +116,9 @@ struct xenisrc { int xi_pirq; int xi_virq; u_int xi_close:1; /* close on unbind? */ - u_int xi_needs_eoi:1; u_int xi_shared:1; /* Shared with other domains. */ + u_int xi_activehi:1; + u_int xi_edgetrigger:1; }; #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) @@ -136,6 +140,9 @@ static void xen_intr_pirq_enable_source( 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,18 +170,20 @@ 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 }; -static struct mtx xen_intr_isrc_lock; -static int xen_intr_isrc_count; -static struct xenisrc *xen_intr_port_to_isrc[NR_EVENT_CHANNELS]; +static struct mtx xen_intr_isrc_lock; +static int xen_intr_auto_vector_count; +static struct xenisrc *xen_intr_port_to_isrc[NR_EVENT_CHANNELS]; +static u_long *xen_intr_pirq_eoi_map; +static boolean_t xen_intr_pirq_eoi_map_enabled; /*------------------------- Private Functions --------------------------------*/ /** @@ -256,7 +265,7 @@ xen_intr_find_unused_isrc(enum evtchn_ty KASSERT(mtx_owned(&xen_intr_isrc_lock), ("Evtchn isrc lock not held")); - for (isrc_idx = 0; isrc_idx < xen_intr_isrc_count; isrc_idx ++) { + for (isrc_idx = 0; isrc_idx < xen_intr_auto_vector_count; isrc_idx ++) { struct xenisrc *isrc; u_int vector; @@ -282,27 +291,33 @@ xen_intr_find_unused_isrc(enum evtchn_ty * 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")); - if (xen_intr_isrc_count > NR_EVENT_CHANNELS) { + if (xen_intr_auto_vector_count > NR_EVENT_CHANNELS) { if (!warned) { warned = 1; printf("xen_intr_alloc: Event channels exhausted.\n"); } 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_auto_vector_count; + xen_intr_auto_vector_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 +403,7 @@ xen_intr_bind_isrc(struct xenisrc **isrc 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, XEN_ALLOCATE_VECTOR); if (isrc == NULL) { mtx_unlock(&xen_intr_isrc_lock); return (ENOSPC); @@ -570,7 +585,8 @@ static int xen_intr_init(void *dummy __unused) { struct xen_intr_pcpu_data *pcpu; - int i; + struct physdev_pirq_eoi_gmfn eoi_gmfn; + int i, rc; if (!xen_domain()) return (0); @@ -591,7 +607,20 @@ xen_intr_init(void *dummy __unused) xen_intr_intrcnt_add(i); } + /* Try to register PIRQ EOI map */ + xen_intr_pirq_eoi_map = malloc(PAGE_SIZE, M_XENINTR, M_WAITOK | M_ZERO); + eoi_gmfn.gmfn = atop(vtophys(xen_intr_pirq_eoi_map)); + rc = HYPERVISOR_physdev_op(PHYSDEVOP_pirq_eoi_gmfn_v2, &eoi_gmfn); + if (rc != 0 && bootverbose) + printf("Xen interrupts: unable to register PIRQ EOI map\n"); + else + xen_intr_pirq_eoi_map_enabled = true; + intr_register_pic(&xen_intr_pic); + intr_register_pic(&xen_intr_pirq_pic); + + if (bootverbose) + printf("Xen interrupt system initialized\n"); return (0); } @@ -696,7 +725,7 @@ xen_intr_resume(struct pic *unused, bool memset(xen_intr_port_to_isrc, 0, sizeof(xen_intr_port_to_isrc)); /* Free unused isrcs and rebind VIRQs and IPIs */ - for (isrc_idx = 0; isrc_idx < xen_intr_isrc_count; isrc_idx++) { + for (isrc_idx = 0; isrc_idx < xen_intr_auto_vector_count; isrc_idx++) { u_int vector; vector = FIRST_EVTCHN_INT + isrc_idx; @@ -916,6 +945,9 @@ xen_intr_pirq_disable_source(struct ints isrc = (struct xenisrc *)base_isrc; evtchn_mask_port(isrc->xi_port); + + if (eoi == PIC_EOI) + xen_intr_pirq_eoi_source(base_isrc); } /* @@ -944,7 +976,7 @@ xen_intr_pirq_eoi_source(struct intsrc * /* XXX Use shared page of flags for this. */ isrc = (struct xenisrc *)base_isrc; - if (isrc->xi_needs_eoi != 0) { + if (test_bit(isrc->xi_pirq, xen_intr_pirq_eoi_map)) { struct physdev_eoi eoi = { .irq = isrc->xi_pirq }; (void)HYPERVISOR_physdev_op(PHYSDEVOP_eoi, &eoi); @@ -957,8 +989,116 @@ xen_intr_pirq_eoi_source(struct intsrc * * \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 (!xen_intr_pirq_eoi_map_enabled) { + 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) { + /* + * Since the dynamic PIRQ EOI map is not available + * mark the PIRQ as needing EOI unconditionally. + */ + set_bit(isrc->xi_pirq, xen_intr_pirq_eoi_map); + } + } + + 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; + + evtchn_mask_port(isrc->xi_port); + + 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 == -XEN_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_activehi = pol == INTR_POLARITY_HIGH ? 1 : 0; + isrc->xi_edgetrigger = trig == INTR_TRIGGER_EDGE ? 1 : 0; + + return (0); } /*--------------------------- Public Functions -------------------------------*/ @@ -1181,6 +1321,50 @@ xen_intr_alloc_and_bind_ipi(device_t dev } int +xen_register_pirq(int vector, enum intr_trigger trig, enum intr_polarity pol) +{ + 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 = pol == INTR_POLARITY_HIGH ? 1 : 0; + isrc->xi_edgetrigger = trig == INTR_TRIGGER_EDGE ? 1 : 0; + + return (0); +} + +int xen_intr_describe(xen_intr_handle_t port_handle, const char *fmt, ...) { char descr[MAXCOMLEN + 1]; Modified: head/sys/xen/xen_intr.h ============================================================================== --- head/sys/xen/xen_intr.h Mon Aug 4 05:50:21 2014 (r269506) +++ head/sys/xen/xen_intr.h Mon Aug 4 08:42:29 2014 (r269507) @@ -159,6 +159,18 @@ int xen_intr_alloc_and_bind_ipi(device_t xen_intr_handle_t *handlep); /** + * Register a physical interrupt vector and setup the interrupt source. + * + * \param vector The global vector to use. + * \param trig Default trigger method. + * \param pol Default polarity of the interrupt. + * + * \returns 0 on success, otherwise an errno. + */ +int xen_register_pirq(int vector, enum intr_trigger trig, + enum intr_polarity pol); + +/** * Unbind an interrupt handler from its interrupt source. * * \param handlep A pointer to the opaque handle that was initialized
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?53df4775.58a4.dae26d3>