xen/intr: move x86 PIC interface to xen_arch_intr.c, introduce wrappers

The x86 PIC interface is very much x86-specific and not used by other
architectures.  Since most of xen_intr.c can be shared with other
architectures, the PIC interface needs to be broken off.

Introduce wrappers for calls into the architecture-dependent interrupt
layer.  All architectures need roughly the same functionality, but the
interface is slightly different between architectures.  Due to the
wrappers being so thin, all of them are implemented as inline in
arch-intr.h.

The original implementation was done by Julien Grall in 2015, but this
has required major updating.

Removal of PVHv1 meant substantial portions disappeared.  The original
implementation took care of moving interrupt allocation to
xen_arch_intr.c, but this has required massive rework and was broken
off.

In the original implementation the wrappers were normal functions.  Some
had empty stubs in xen_intr.c and were removed.

Reviewed by: royger
Submitted by: Elliott Mitchell <ehem+freebsd@m5p.com>
Original implementation: Julien Grall <julien@xen.org>, 2015-10-20 09:14:56
Differential Revision: https://reviews.freebsd.org/D30909
This commit is contained in:
Julien Grall 2015-10-20 17:14:56 +01:00 committed by Roger Pau Monné
parent 373301019f
commit 2d795ab1ea
4 changed files with 268 additions and 148 deletions

View File

@ -60,4 +60,34 @@ struct xenisrc {
volatile u_int xi_refcount;
};
/***************** Functions called by the architecture code *****************/
extern void xen_intr_resume(void);
extern void xen_intr_enable_source(struct xenisrc *isrc);
extern void xen_intr_disable_source(struct xenisrc *isrc);
extern void xen_intr_enable_intr(struct xenisrc *isrc);
extern void xen_intr_disable_intr(struct xenisrc *isrc);
extern int xen_intr_assign_cpu(struct xenisrc *isrc, u_int to_cpu);
/******************* Functions implemented by each architecture **************/
#if 0
/*
* These are sample prototypes, the architecture should include its own in
* <machine/xen/arch-intr.h>. The architecture may implement these as inline.
*/
void xen_arch_intr_init(void);
u_long xen_arch_intr_execute_handlers(struct xenisrc *isrc,
struct trapframe *frame);
int xen_arch_intr_add_handler(const char *name,
driver_filter_t filter, driver_intr_t handler, void *arg,
enum intr_type flags, struct xenisrc *isrc,
void **cookiep);
int xen_arch_intr_describe(struct xenisrc *isrc, void *cookie,
const char *descr);
int xen_arch_intr_remove_handler(struct xenisrc *isrc,
void *cookie);
int xen_arch_intr_event_bind(struct xenisrc *isrc, u_int cpu);
#endif
#endif /* _XEN_INTR_INTERNAL_H_ */

View File

@ -36,6 +36,51 @@ typedef struct {
u_int vector; /* Global isrc vector number */
} xen_arch_isrc_t;
extern struct pic xen_intr_pic;
#include <dev/xen/bus/intr-internal.h>
/******************************* ARCH wrappers *******************************/
extern void xen_arch_intr_init(void);
static inline u_long
xen_arch_intr_execute_handlers(struct xenisrc *isrc, struct trapframe *frame)
{
intr_execute_handlers(&isrc->xi_arch.intsrc, frame);
return (0);
}
static inline int
xen_arch_intr_add_handler(const char *name, driver_filter_t filter,
driver_intr_t handler, void *arg, enum intr_type flags,
struct xenisrc *isrc, void **cookiep)
{
return (intr_add_handler(name, isrc->xi_arch.vector, filter, handler,
arg, flags, cookiep, 0));
}
static inline int
xen_arch_intr_describe(struct xenisrc *isrc, void *cookie, const char *descr)
{
return (intr_describe(isrc->xi_arch.vector, cookie, descr));
}
static inline int
xen_arch_intr_remove_handler(struct xenisrc *isrc, void *cookie)
{
return (intr_remove_handler(cookie));
}
static inline int
xen_arch_intr_event_bind(struct xenisrc *isrc, u_int cpu)
{
return (intr_event_bind(isrc->xi_arch.intsrc.is_event, cpu));
}
#endif /* _MACHINE__XEN_ARCH_INTR_H_ */

View File

@ -1,6 +1,7 @@
/*-
* SPDX-License-Identifier: MIT OR GPL-2.0-only
*
* Copyright © 2015 Julien Grall
* Copyright © 2013 Spectra Logic Corporation
* Copyright © 2018 John Baldwin/The FreeBSD Foundation
* Copyright © 2019 Roger Pau Monné/Citrix Systems R&D
@ -32,8 +33,14 @@
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/limits.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/interrupt.h>
#include <sys/pcpu.h>
#include <sys/proc.h>
#include <sys/smp.h>
@ -41,8 +48,8 @@ __FBSDID("$FreeBSD$");
#include <xen/xen-os.h>
#include <xen/xen_intr.h>
#include <machine/xen/arch-intr.h>
#include <x86/intr_machdep.h>
#include <x86/apicvar.h>
/************************ Xen x86 interrupt interface ************************/
@ -101,3 +108,165 @@ xen_arch_intr_handle_upcall(struct trapframe *trap_frame)
critical_exit();
}
/******************************** EVTCHN PIC *********************************/
static void
xen_intr_pic_enable_source(struct intsrc *isrc)
{
_Static_assert(offsetof(struct xenisrc, xi_arch.intsrc) == 0,
"xi_arch MUST be at top of xenisrc for x86");
xen_intr_enable_source((struct xenisrc *)isrc);
}
/*
* Perform any necessary end-of-interrupt acknowledgements.
*
* \param isrc The interrupt source to EOI.
*/
static void
xen_intr_pic_disable_source(struct intsrc *isrc, int eoi)
{
_Static_assert(offsetof(struct xenisrc, xi_arch.intsrc) == 0,
"xi_arch MUST be at top of xenisrc for x86");
xen_intr_disable_source((struct xenisrc *)isrc);
}
static void
xen_intr_pic_eoi_source(struct intsrc *isrc)
{
/* Nothing to do on end-of-interrupt */
}
static void
xen_intr_pic_enable_intr(struct intsrc *isrc)
{
_Static_assert(offsetof(struct xenisrc, xi_arch.intsrc) == 0,
"xi_arch MUST be at top of xenisrc for x86");
xen_intr_enable_intr((struct xenisrc *)isrc);
}
static void
xen_intr_pic_disable_intr(struct intsrc *isrc)
{
_Static_assert(offsetof(struct xenisrc, xi_arch.intsrc) == 0,
"xi_arch MUST be at top of xenisrc for x86");
xen_intr_disable_intr((struct xenisrc *)isrc);
}
/**
* Determine the global interrupt vector number for
* a Xen interrupt source.
*
* \param isrc The interrupt source to query.
*
* \return The vector number corresponding to the given interrupt source.
*/
static int
xen_intr_pic_vector(struct intsrc *isrc)
{
_Static_assert(offsetof(struct xenisrc, xi_arch.intsrc) == 0,
"xi_arch MUST be at top of xenisrc for x86");
return (((struct xenisrc *)isrc)->xi_arch.vector);
}
/**
* Determine whether or not interrupt events are pending on the
* the given interrupt source.
*
* \param isrc The interrupt source to query.
*
* \returns 0 if no events are pending, otherwise non-zero.
*/
static int
xen_intr_pic_source_pending(struct intsrc *isrc)
{
/*
* EventChannels are edge triggered and never masked.
* There can be no pending events.
*/
return (0);
}
/**
* Prepare this PIC for system suspension.
*/
static void
xen_intr_pic_suspend(struct pic *pic)
{
/* Nothing to do on suspend */
}
static void
xen_intr_pic_resume(struct pic *pic, bool suspend_cancelled)
{
if (!suspend_cancelled)
xen_intr_resume();
}
/**
* 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_pic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
enum intr_polarity pol)
{
/* Configuration is only possible via the evtchn apis. */
return (ENODEV);
}
static int
xen_intr_pic_assign_cpu(struct intsrc *isrc, u_int apic_id)
{
_Static_assert(offsetof(struct xenisrc, xi_arch.intsrc) == 0,
"xi_arch MUST be at top of xenisrc for x86");
return (xen_intr_assign_cpu((struct xenisrc *)isrc,
apic_cpuid(apic_id)));
}
/**
* PIC interface for all event channel port types except physical IRQs.
*/
struct pic xen_intr_pic = {
.pic_enable_source = xen_intr_pic_enable_source,
.pic_disable_source = xen_intr_pic_disable_source,
.pic_eoi_source = xen_intr_pic_eoi_source,
.pic_enable_intr = xen_intr_pic_enable_intr,
.pic_disable_intr = xen_intr_pic_disable_intr,
.pic_vector = xen_intr_pic_vector,
.pic_source_pending = xen_intr_pic_source_pending,
.pic_suspend = xen_intr_pic_suspend,
.pic_resume = xen_intr_pic_resume,
.pic_config_intr = xen_intr_pic_config_intr,
.pic_assign_cpu = xen_intr_pic_assign_cpu,
};
/******************************* ARCH wrappers *******************************/
void
xen_arch_intr_init(void)
{
int error;
error = intr_register_pic(&xen_intr_pic);
if (error != 0)
panic("%s(): failed registering Xen/x86 PIC, error=%d\n",
__func__, error);
}

View File

@ -117,37 +117,6 @@ DPCPU_DECLARE(struct vcpu_info *, vcpu_info);
#define INVALID_EVTCHN (~(evtchn_port_t)0) /* Invalid event channel */
#define is_valid_evtchn(x) ((uintmax_t)(x) < NR_EVENT_CHANNELS)
static void xen_intr_suspend(struct pic *);
static void xen_intr_resume(struct pic *, bool suspend_cancelled);
static void xen_intr_enable_source(struct intsrc *isrc);
static void xen_intr_disable_source(struct intsrc *isrc, int eoi);
static void xen_intr_eoi_source(struct intsrc *isrc);
static void xen_intr_enable_intr(struct intsrc *isrc);
static void xen_intr_disable_intr(struct intsrc *isrc);
static int xen_intr_vector(struct intsrc *isrc);
static int xen_intr_source_pending(struct intsrc *isrc);
static int xen_intr_config_intr(struct intsrc *isrc,
enum intr_trigger trig, enum intr_polarity pol);
static int xen_intr_assign_cpu(struct intsrc *isrc, u_int to_cpu);
static int xen_intr_pic_assign_cpu(struct intsrc *isrc, u_int apic_id);
/**
* PIC interface for all event channel port types except physical IRQs.
*/
struct pic xen_intr_pic = {
.pic_enable_source = xen_intr_enable_source,
.pic_disable_source = xen_intr_disable_source,
.pic_eoi_source = xen_intr_eoi_source,
.pic_enable_intr = xen_intr_enable_intr,
.pic_disable_intr = xen_intr_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_assign_cpu = xen_intr_pic_assign_cpu,
};
/*
* Lock for interrupt core data.
*
@ -413,8 +382,7 @@ xen_intr_bind_isrc(struct xenisrc **isrcp, evtchn_port_t local_port,
* unless specified otherwise, so shuffle them to balance
* the interrupt load.
*/
xen_intr_assign_cpu(&isrc->xi_arch.intsrc,
apic_cpuid(intr_next_cpu(0)));
xen_intr_assign_cpu(isrc, apic_cpuid(intr_next_cpu(0)));
}
#endif
@ -544,8 +512,7 @@ xen_intr_handle_upcall(void *unused __unused)
("Received unexpected event on vCPU#%u, event bound to vCPU#%u",
PCPU_GET(cpuid), isrc->xi_cpu));
intr_execute_handlers(&isrc->xi_arch.intsrc,
trap_frame);
xen_arch_intr_execute_handlers(isrc, trap_frame);
/*
* If this is the final port processed,
@ -609,7 +576,7 @@ xen_intr_init(void *dummy __unused)
for (i = 0; i < nitems(s->evtchn_mask); i++)
atomic_store_rel_long(&s->evtchn_mask[i], ~0);
intr_register_pic(&xen_intr_pic);
xen_arch_intr_init();
if (bootverbose)
printf("Xen interrupt system initialized\n");
@ -629,13 +596,6 @@ xen_intr_alloc_irqs(void)
}
/*--------------------------- Common PIC Functions ---------------------------*/
/**
* Prepare this PIC for system suspension.
*/
static void
xen_intr_suspend(struct pic *unused)
{
}
static void
xen_rebind_ipi(struct xenisrc *isrc)
@ -699,7 +659,7 @@ xen_intr_rebind_isrc(struct xenisrc *isrc)
#ifdef SMP
isrc->xi_cpu = 0;
error = xen_intr_assign_cpu(&isrc->xi_arch.intsrc, cpu);
error = xen_intr_assign_cpu(isrc, cpu);
if (error)
panic("%s(): unable to rebind Xen channel %u to vCPU%u: %d",
__func__, isrc->xi_port, cpu, error);
@ -713,16 +673,13 @@ xen_intr_rebind_isrc(struct xenisrc *isrc)
/**
* Return this PIC to service after being suspended.
*/
static void
xen_intr_resume(struct pic *unused, bool suspend_cancelled)
void
xen_intr_resume(void)
{
shared_info_t *s = HYPERVISOR_shared_info;
u_int isrc_idx;
int i;
if (suspend_cancelled)
return;
/* Reset the per-CPU masks */
CPU_FOREACH(i) {
struct xen_intr_pcpu_data *pcpu;
@ -767,65 +724,13 @@ xen_intr_resume(struct pic *unused, bool suspend_cancelled)
*
* \param isrc The interrupt source to disable.
*/
static void
xen_intr_disable_intr(struct intsrc *base_isrc)
void
xen_intr_disable_intr(struct xenisrc *isrc)
{
struct xenisrc *isrc = (struct xenisrc *)base_isrc;
evtchn_mask_port(isrc->xi_port);
}
/**
* Determine the global interrupt vector number for
* a Xen interrupt source.
*
* \param isrc The interrupt source to query.
*
* \return The vector number corresponding to the given interrupt source.
*/
static int
xen_intr_vector(struct intsrc *base_isrc)
{
struct xenisrc *isrc = (struct xenisrc *)base_isrc;
return (isrc->xi_arch.vector);
}
/**
* Determine whether or not interrupt events are pending on the
* the given interrupt source.
*
* \param isrc The interrupt source to query.
*
* \returns 0 if no events are pending, otherwise non-zero.
*/
static int
xen_intr_source_pending(struct intsrc *isrc)
{
/*
* EventChannels are edge triggered and never masked.
* There can be no pending events.
*/
return (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_config_intr(struct intsrc *isrc, enum intr_trigger trig,
enum intr_polarity pol)
{
/* Configuration is only possible via the evtchn apis. */
return (ENODEV);
}
/**
* Configure CPU affinity for interrupt source event delivery.
*
@ -834,12 +739,11 @@ xen_intr_config_intr(struct intsrc *isrc, enum intr_trigger trig,
*
* \returns 0 if successful, otherwise an errno.
*/
static int
xen_intr_assign_cpu(struct intsrc *base_isrc, u_int to_cpu)
int
xen_intr_assign_cpu(struct xenisrc *isrc, u_int to_cpu)
{
#ifdef SMP
struct evtchn_bind_vcpu bind_vcpu;
struct xenisrc *isrc;
u_int vcpu_id = XEN_CPUID_TO_VCPUID(to_cpu);
int error, masked;
@ -847,7 +751,6 @@ xen_intr_assign_cpu(struct intsrc *base_isrc, u_int to_cpu)
return (EOPNOTSUPP);
mtx_lock(&xen_intr_isrc_lock);
isrc = (struct xenisrc *)base_isrc;
if (!is_valid_evtchn(isrc->xi_port)) {
mtx_unlock(&xen_intr_isrc_lock);
return (EINVAL);
@ -894,28 +797,15 @@ xen_intr_assign_cpu(struct intsrc *base_isrc, u_int to_cpu)
#endif
}
/* Wrapper of xen_intr_assign_cpu to use as pic callbacks */
static int
xen_intr_pic_assign_cpu(struct intsrc *isrc, u_int apic_id)
{
return (xen_intr_assign_cpu(isrc, apic_cpuid(apic_id)));
}
/*------------------- Virtual Interrupt Source PIC Functions -----------------*/
/*
* Mask a level triggered interrupt source.
*
* \param isrc The interrupt source to mask (if necessary).
* \param eoi If non-zero, perform any necessary end-of-interrupt
* acknowledgements.
*/
static void
xen_intr_disable_source(struct intsrc *base_isrc, int eoi)
void
xen_intr_disable_source(struct xenisrc *isrc)
{
struct xenisrc *isrc;
isrc = (struct xenisrc *)base_isrc;
/*
* NB: checking if the event channel is already masked is
@ -933,36 +823,22 @@ xen_intr_disable_source(struct intsrc *base_isrc, int eoi)
*
* \param isrc The interrupt source to unmask (if necessary).
*/
static void
xen_intr_enable_source(struct intsrc *base_isrc)
void
xen_intr_enable_source(struct xenisrc *isrc)
{
struct xenisrc *isrc;
isrc = (struct xenisrc *)base_isrc;
if (isrc->xi_masked == 0)
evtchn_unmask_port(isrc->xi_port);
}
/*
* Perform any necessary end-of-interrupt acknowledgements.
*
* \param isrc The interrupt source to EOI.
*/
static void
xen_intr_eoi_source(struct intsrc *base_isrc)
{
}
/*
* Enable and unmask the interrupt source.
*
* \param isrc The interrupt source to enable.
*/
static void
xen_intr_enable_intr(struct intsrc *base_isrc)
void
xen_intr_enable_intr(struct xenisrc *isrc)
{
struct xenisrc *isrc = (struct xenisrc *)base_isrc;
evtchn_unmask_port(isrc->xi_port);
}
@ -1091,7 +967,7 @@ xen_intr_bind_virq(device_t dev, u_int virq, u_int cpu,
#ifdef SMP
if (error == 0)
error = intr_event_bind(isrc->xi_arch.intsrc.is_event, cpu);
error = xen_arch_intr_event_bind(isrc, cpu);
#endif
if (error != 0) {
@ -1111,7 +987,7 @@ xen_intr_bind_virq(device_t dev, u_int virq, u_int cpu,
* masks manually so events can't fire on the wrong cpu
* during AP startup.
*/
xen_intr_assign_cpu(&isrc->xi_arch.intsrc, cpu);
xen_intr_assign_cpu(isrc, cpu);
}
#endif
@ -1167,7 +1043,7 @@ xen_intr_alloc_and_bind_ipi(u_int cpu, driver_filter_t filter,
* masks manually so events can't fire on the wrong cpu
* during AP startup.
*/
xen_intr_assign_cpu(&isrc->xi_arch.intsrc, cpu);
xen_intr_assign_cpu(isrc, cpu);
}
/*
@ -1195,7 +1071,7 @@ xen_intr_describe(xen_intr_handle_t port_handle, const char *fmt, ...)
va_start(ap, fmt);
vsnprintf(descr, sizeof(descr), fmt, ap);
va_end(ap);
return (intr_describe(isrc->xi_arch.vector, isrc->xi_cookie, descr));
return (xen_arch_intr_describe(isrc, isrc->xi_cookie, descr));
}
void
@ -1219,7 +1095,7 @@ xen_intr_unbind(xen_intr_handle_t *port_handlep)
mtx_unlock(&xen_intr_isrc_lock);
if (isrc->xi_cookie != NULL)
intr_remove_handler(isrc->xi_cookie);
xen_arch_intr_remove_handler(isrc, isrc->xi_cookie);
xen_intr_release_isrc(isrc);
}
@ -1262,8 +1138,8 @@ xen_intr_add_handler(const char *name, driver_filter_t filter,
if (isrc == NULL || isrc->xi_cookie != NULL)
return (EINVAL);
error = intr_add_handler(name, isrc->xi_arch.vector, filter, handler,
arg, flags|INTR_EXCL, &isrc->xi_cookie, 0);
error = xen_arch_intr_add_handler(name, filter, handler, arg,
flags | INTR_EXCL, isrc, &isrc->xi_cookie);
if (error != 0)
printf("%s: %s: add handler failed: %d\n", name, __func__,
error);