xen/intr: move interrupt allocation/release to architecture
Simply moving the interrupt allocation and release functions into files which belong to the architecture. Since x86 interrupt handling is quite distinct from other architectures, this is a crucial necessary step. Identifying the border between x86 and architecture-independent is actually quite tricky. Similarly, getting the prototypes for the border right is also quite tricky. Inspired by the work of Julien Grall <julien@xen.org>, 2015-10-20 09:14:56, but heavily adjusted. Reviewed by: royger Differential Revision: https://reviews.freebsd.org/D30936
This commit is contained in:
parent
2d795ab1ea
commit
6699c22c1c
@ -77,6 +77,9 @@ extern int xen_intr_assign_cpu(struct xenisrc *isrc, u_int to_cpu);
|
||||
* <machine/xen/arch-intr.h>. The architecture may implement these as inline.
|
||||
*/
|
||||
void xen_arch_intr_init(void);
|
||||
struct xenisrc *xen_arch_intr_alloc(void);
|
||||
void xen_arch_intr_release(struct xenisrc *isrc);
|
||||
u_int xen_arch_intr_next_cpu(struct xenisrc *isrc);
|
||||
u_long xen_arch_intr_execute_handlers(struct xenisrc *isrc,
|
||||
struct trapframe *frame);
|
||||
int xen_arch_intr_add_handler(const char *name,
|
||||
|
@ -30,20 +30,29 @@
|
||||
#define _MACHINE__XEN_ARCH_INTR_H_
|
||||
|
||||
#include <x86/intr_machdep.h>
|
||||
#include <x86/apicvar.h>
|
||||
|
||||
typedef struct {
|
||||
struct intsrc intsrc; /* @TOP -> *xen_arch_isrc */
|
||||
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);
|
||||
|
||||
extern struct xenisrc *xen_arch_intr_alloc(void);
|
||||
extern void xen_arch_intr_release(struct xenisrc *isrc);
|
||||
|
||||
static inline u_int
|
||||
xen_arch_intr_next_cpu(struct xenisrc *isrc)
|
||||
{
|
||||
|
||||
return (apic_cpuid(intr_next_cpu(0)));
|
||||
}
|
||||
|
||||
static inline u_long
|
||||
xen_arch_intr_execute_handlers(struct xenisrc *isrc, struct trapframe *frame)
|
||||
{
|
||||
|
@ -111,6 +111,41 @@ xen_arch_intr_handle_upcall(struct trapframe *trap_frame)
|
||||
|
||||
/******************************** EVTCHN PIC *********************************/
|
||||
|
||||
static MALLOC_DEFINE(M_XENINTR, "xen_intr", "Xen Interrupt Services");
|
||||
|
||||
/*
|
||||
* Lock for x86-related structures. Notably modifying
|
||||
* xen_intr_auto_vector_count, and allocating interrupts require this lock be
|
||||
* held.
|
||||
*/
|
||||
static struct mtx xen_intr_x86_lock;
|
||||
|
||||
static u_int first_evtchn_irq;
|
||||
|
||||
static u_int xen_intr_auto_vector_count;
|
||||
|
||||
/*
|
||||
* list of released isrcs
|
||||
* This is meant to overlay struct xenisrc, with only the xen_arch_isrc_t
|
||||
* portion being preserved, everything else can be wiped.
|
||||
*/
|
||||
struct avail_list {
|
||||
xen_arch_isrc_t preserve;
|
||||
SLIST_ENTRY(avail_list) free;
|
||||
};
|
||||
static SLIST_HEAD(free, avail_list) avail_list =
|
||||
SLIST_HEAD_INITIALIZER(avail_list);
|
||||
|
||||
void
|
||||
xen_intr_alloc_irqs(void)
|
||||
{
|
||||
|
||||
if (num_io_irqs > UINT_MAX - NR_EVENT_CHANNELS)
|
||||
panic("IRQ allocation overflow (num_msi_irqs too high?)");
|
||||
first_evtchn_irq = num_io_irqs;
|
||||
num_io_irqs += NR_EVENT_CHANNELS;
|
||||
}
|
||||
|
||||
static void
|
||||
xen_intr_pic_enable_source(struct intsrc *isrc)
|
||||
{
|
||||
@ -244,7 +279,7 @@ 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 = {
|
||||
static 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,
|
||||
@ -265,8 +300,86 @@ xen_arch_intr_init(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
mtx_init(&xen_intr_x86_lock, "xen-x86-table-lock", NULL, MTX_DEF);
|
||||
|
||||
error = intr_register_pic(&xen_intr_pic);
|
||||
if (error != 0)
|
||||
panic("%s(): failed registering Xen/x86 PIC, error=%d\n",
|
||||
__func__, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a Xen interrupt source object.
|
||||
*
|
||||
* \param type The type of interrupt source to create.
|
||||
*
|
||||
* \return A pointer to a newly allocated Xen interrupt source
|
||||
* object or NULL.
|
||||
*/
|
||||
struct xenisrc *
|
||||
xen_arch_intr_alloc(void)
|
||||
{
|
||||
static int warned;
|
||||
struct xenisrc *isrc;
|
||||
unsigned int vector;
|
||||
int error;
|
||||
|
||||
mtx_lock(&xen_intr_x86_lock);
|
||||
isrc = (struct xenisrc *)SLIST_FIRST(&avail_list);
|
||||
if (isrc != NULL) {
|
||||
SLIST_REMOVE_HEAD(&avail_list, free);
|
||||
mtx_unlock(&xen_intr_x86_lock);
|
||||
|
||||
KASSERT(isrc->xi_arch.intsrc.is_pic == &xen_intr_pic,
|
||||
("interrupt not owned by Xen code?"));
|
||||
|
||||
KASSERT(isrc->xi_arch.intsrc.is_handlers == 0,
|
||||
("Free evtchn still has handlers"));
|
||||
|
||||
return (isrc);
|
||||
}
|
||||
|
||||
if (xen_intr_auto_vector_count >= NR_EVENT_CHANNELS) {
|
||||
if (!warned) {
|
||||
warned = 1;
|
||||
printf("%s: Xen interrupts exhausted.\n", __func__);
|
||||
}
|
||||
mtx_unlock(&xen_intr_x86_lock);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
vector = first_evtchn_irq + 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_x86_lock);
|
||||
isrc = malloc(sizeof(*isrc), M_XENINTR, M_WAITOK | M_ZERO);
|
||||
isrc->xi_arch.intsrc.is_pic = &xen_intr_pic;
|
||||
isrc->xi_arch.vector = vector;
|
||||
error = intr_register_source(&isrc->xi_arch.intsrc);
|
||||
if (error != 0)
|
||||
panic("%s(): failed registering interrupt %u, error=%d\n",
|
||||
__func__, vector, error);
|
||||
|
||||
return (isrc);
|
||||
}
|
||||
|
||||
void
|
||||
xen_arch_intr_release(struct xenisrc *isrc)
|
||||
{
|
||||
|
||||
KASSERT(isrc->xi_arch.intsrc.is_handlers == 0,
|
||||
("Release called, but xenisrc still in use"));
|
||||
|
||||
_Static_assert(sizeof(struct xenisrc) >= sizeof(struct avail_list),
|
||||
"unused structure MUST be no larger than in-use structure");
|
||||
_Static_assert(offsetof(struct xenisrc, xi_arch) ==
|
||||
offsetof(struct avail_list, preserve),
|
||||
"unused structure does not properly overlay in-use structure");
|
||||
|
||||
mtx_lock(&xen_intr_x86_lock);
|
||||
SLIST_INSERT_HEAD(&avail_list, (struct avail_list *)isrc, free);
|
||||
mtx_unlock(&xen_intr_x86_lock);
|
||||
}
|
||||
|
@ -38,7 +38,6 @@ __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>
|
||||
@ -68,17 +67,6 @@ __FBSDID("$FreeBSD$");
|
||||
#include <ddb/ddb.h>
|
||||
#endif
|
||||
|
||||
static MALLOC_DEFINE(M_XENINTR, "xen_intr", "Xen Interrupt Services");
|
||||
|
||||
/*
|
||||
* Lock for x86-related structures. Notably modifying
|
||||
* xen_intr_auto_vector_count, and allocating interrupts require this lock be
|
||||
* held.
|
||||
*/
|
||||
static struct mtx xen_intr_x86_lock;
|
||||
|
||||
static u_int first_evtchn_irq;
|
||||
|
||||
/**
|
||||
* Per-cpu event channel processing state.
|
||||
*/
|
||||
@ -128,21 +116,8 @@ DPCPU_DECLARE(struct vcpu_info *, vcpu_info);
|
||||
* Acquire/release operations for isrc->xi_refcount require this lock be held.
|
||||
*/
|
||||
static struct mtx xen_intr_isrc_lock;
|
||||
static u_int xen_intr_auto_vector_count;
|
||||
static struct xenisrc *xen_intr_port_to_isrc[NR_EVENT_CHANNELS];
|
||||
|
||||
/*
|
||||
* list of released isrcs
|
||||
* This is meant to overlay struct xenisrc, with only the xen_arch_isrc_t
|
||||
* portion being preserved, everything else can be wiped.
|
||||
*/
|
||||
struct avail_list {
|
||||
xen_arch_isrc_t preserve;
|
||||
SLIST_ENTRY(avail_list) free;
|
||||
};
|
||||
static SLIST_HEAD(free, avail_list) avail_list =
|
||||
SLIST_HEAD_INITIALIZER(avail_list);
|
||||
|
||||
/*------------------------- Private Functions --------------------------------*/
|
||||
|
||||
/**
|
||||
@ -220,64 +195,6 @@ evtchn_cpu_unmask_port(u_int cpu, evtchn_port_t port)
|
||||
xen_set_bit(port, pcpu->evtchn_enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a Xen interrupt source object.
|
||||
*
|
||||
* \param type The type of interrupt source to create.
|
||||
*
|
||||
* \return A pointer to a newly allocated Xen interrupt source
|
||||
* object or NULL.
|
||||
*/
|
||||
static struct xenisrc *
|
||||
xen_intr_alloc_isrc(void)
|
||||
{
|
||||
static int warned;
|
||||
struct xenisrc *isrc;
|
||||
unsigned int vector;
|
||||
int error;
|
||||
|
||||
mtx_lock(&xen_intr_x86_lock);
|
||||
isrc = (struct xenisrc *)SLIST_FIRST(&avail_list);
|
||||
if (isrc != NULL) {
|
||||
SLIST_REMOVE_HEAD(&avail_list, free);
|
||||
mtx_unlock(&xen_intr_x86_lock);
|
||||
|
||||
KASSERT(isrc->xi_arch.intsrc.is_pic == &xen_intr_pic,
|
||||
("interrupt not owned by Xen code?"));
|
||||
|
||||
KASSERT(isrc->xi_arch.intsrc.is_handlers == 0,
|
||||
("Free evtchn still has handlers"));
|
||||
|
||||
return (isrc);
|
||||
}
|
||||
|
||||
if (xen_intr_auto_vector_count >= NR_EVENT_CHANNELS) {
|
||||
if (!warned) {
|
||||
warned = 1;
|
||||
printf("%s: Xen interrupts exhausted.\n", __func__);
|
||||
}
|
||||
mtx_unlock(&xen_intr_x86_lock);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
vector = first_evtchn_irq + 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_x86_lock);
|
||||
isrc = malloc(sizeof(*isrc), M_XENINTR, M_WAITOK | M_ZERO);
|
||||
isrc->xi_arch.intsrc.is_pic = &xen_intr_pic;
|
||||
isrc->xi_arch.vector = vector;
|
||||
error = intr_register_source(&isrc->xi_arch.intsrc);
|
||||
if (error != 0)
|
||||
panic("%s(): failed registering interrupt %u, error=%d\n",
|
||||
__func__, vector, error);
|
||||
|
||||
return (isrc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to free an active Xen interrupt source object.
|
||||
*
|
||||
@ -289,8 +206,6 @@ static int
|
||||
xen_intr_release_isrc(struct xenisrc *isrc)
|
||||
{
|
||||
|
||||
KASSERT(isrc->xi_arch.intsrc.is_handlers == 0,
|
||||
("Release called, but xenisrc still in use"));
|
||||
mtx_lock(&xen_intr_isrc_lock);
|
||||
if (is_valid_evtchn(isrc->xi_port)) {
|
||||
evtchn_mask_port(isrc->xi_port);
|
||||
@ -312,15 +227,7 @@ xen_intr_release_isrc(struct xenisrc *isrc)
|
||||
/* not reachable from xen_intr_port_to_isrc[], unlock */
|
||||
mtx_unlock(&xen_intr_isrc_lock);
|
||||
|
||||
_Static_assert(sizeof(struct xenisrc) >= sizeof(struct avail_list),
|
||||
"unused structure MUST be no larger than in-use structure");
|
||||
_Static_assert(offsetof(struct xenisrc, xi_arch) ==
|
||||
offsetof(struct avail_list, preserve),
|
||||
"unused structure does not properly overlay in-use structure");
|
||||
|
||||
mtx_lock(&xen_intr_x86_lock);
|
||||
SLIST_INSERT_HEAD(&avail_list, (struct avail_list *)isrc, free);
|
||||
mtx_unlock(&xen_intr_x86_lock);
|
||||
xen_arch_intr_release(isrc);
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -361,7 +268,7 @@ xen_intr_bind_isrc(struct xenisrc **isrcp, evtchn_port_t local_port,
|
||||
}
|
||||
*port_handlep = NULL;
|
||||
|
||||
isrc = xen_intr_alloc_isrc();
|
||||
isrc = xen_arch_intr_alloc();
|
||||
if (isrc == NULL)
|
||||
return (ENOSPC);
|
||||
|
||||
@ -382,7 +289,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, apic_cpuid(intr_next_cpu(0)));
|
||||
xen_intr_assign_cpu(isrc, xen_arch_intr_next_cpu(isrc));
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -561,8 +468,6 @@ xen_intr_init(void *dummy __unused)
|
||||
|
||||
mtx_init(&xen_intr_isrc_lock, "xen-irq-lock", NULL, MTX_DEF);
|
||||
|
||||
mtx_init(&xen_intr_x86_lock, "xen-x86-table-lock", NULL, MTX_DEF);
|
||||
|
||||
/*
|
||||
* Set the per-cpu mask of CPU#0 to enable all, since by default all
|
||||
* event channels are bound to CPU#0.
|
||||
@ -585,16 +490,6 @@ xen_intr_init(void *dummy __unused)
|
||||
}
|
||||
SYSINIT(xen_intr_init, SI_SUB_INTR, SI_ORDER_SECOND, xen_intr_init, NULL);
|
||||
|
||||
void
|
||||
xen_intr_alloc_irqs(void)
|
||||
{
|
||||
|
||||
if (num_io_irqs > UINT_MAX - NR_EVENT_CHANNELS)
|
||||
panic("IRQ allocation overflow (num_msi_irqs too high?)");
|
||||
first_evtchn_irq = num_io_irqs;
|
||||
num_io_irqs += NR_EVENT_CHANNELS;
|
||||
}
|
||||
|
||||
/*--------------------------- Common PIC Functions ---------------------------*/
|
||||
|
||||
static void
|
||||
|
Loading…
Reference in New Issue
Block a user