avintr.c revision e8bb33d8a52501da2c03bbcb5fddf3239b74ad8c
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Autovectored Interrupt Configuration and Deconfiguration
*/
#include <sys/machlock.h>
#include <sys/machsystm.h>
#include <sys/smp_impldefs.h>
#include <sys/ddi_impldefs.h>
#ifdef __xpv
#include <sys/evtchn_impl.h>
#endif
typedef struct av_softinfo {
dev_info_t *dip);
/*
* Arrange for a driver to be called when a particular
* auto-vectored interrupt occurs.
* NOTE: if a device can generate interrupts on more than
* one level, or if a driver services devices that interrupt
* on more than one level, then the driver should install
* itself on each of those levels.
*/
static char badsoft[] =
"add_avintr: bad soft interrupt level %d for driver '%s'\n";
static char multilevel[] =
"!IRQ%d is being shared by drivers with different interrupt levels.\n"
"This may result in reduced system performance.";
static char multilevel2[] =
"Cannot register interrupt for '%s' device at IPL %d because it\n"
"conflicts with another device using the same vector %d with an IPL\n"
"of %d. Reconfigure the conflicting devices to use different vectors.";
#ifdef __xpv
#else
#define MAX_VECT 256
#endif
/*
* These are software interrupt handlers dedicated to ddi timer.
* The interrupt levels up to 10 are supported, but high interrupts
* must not be used there.
*/
};
/*
* the current CPU
*/
void
{
}
{
if (check_all)
else
}
/*
* This is the wrapper function which is generally used to set a softint
* pending
*/
void
{
}
/*
* This is kmdb's private entry point to setsoftint called from kdi_siron
* It first sets our av softint pending bit for the current CPU,
* then it sets the CPU softint pending bit for pri.
*/
void
{
}
/*
* register nmi interrupt routine. The first arg is used only to order
* various nmi interrupt service routines in the chain. Higher lvls will
* be called first
*/
int
{
return (0);
}
if (!nmivect) {
return (1);
}
/* find where it goes in list */
/*
* already in list
* So? Somebody added the same interrupt twice.
*/
name);
return (0);
}
if (p->av_prilevel < lvl) {
if (p == nmivect) { /* it's at head of list */
} else {
}
return (1);
}
prev = p;
}
/* didn't find it, add it to the end */
return (1);
}
/*
* register a hardware interrupt handler.
*/
int
{
avfunc f;
int s, vectindex; /* save old spl value */
printf("Attempt to add null vect for %s on vector %d\n",
return (0);
}
/*
* "hi_pri == 0" implies all entries on list are "unused",
* which means that it's OK to just insert this one.
*/
hi_pri);
return (0);
}
}
s = splhi();
/*
* do what ever machine specific things are necessary
* to set priority level (e.g. set picmasks)
*/
splx(s);
return (1);
}
void
{
struct autovec *p;
if (p->av_intr_id == intr_id) {
target = p;
break;
}
}
return;
}
/*
* Register a software interrupt handler
*/
int
{
int slvl;
printf("Attempt to add null intr_id for %s on level %d\n",
return (0);
}
printf("Attempt to add null handler for %s on level %d\n",
return (0);
}
return (0);
}
hdlp->ih_pending =
}
return (1);
}
/* insert an interrupt vector into chain */
static void
{
/*
* Protect rewrites of the list
*/
return;
}
/* find where it goes in list */
p->av_intarg1 = arg1;
p->av_intarg2 = arg2;
p->av_intr_id = intr_id;
p->av_prilevel = pri_level;
}
}
/*
* To prevent calling service routine before args
* and ticksp are ready fill in vector last.
*/
p->av_vector = f;
return;
}
}
/* insert new intpt at beginning of chain */
}
}
}
static int
{
int slvl;
return (0);
return (1);
}
return (0);
}
if (rem_softinfo) {
}
return (1);
}
int
{
int ret;
if (ret) {
B_FALSE);
}
return (ret);
}
/*
* Remove a driver from the autovector list.
*/
int
{
}
void
{
avfunc f;
int s, vectindex; /* save old spl value */
return;
s = splhi();
splx(s);
}
/*
* After having made a change to an autovector list, wait until we have
* seen each cpu not executing an interrupt at that level--so we know our
* change has taken effect completely (no old state in registers, etc).
*/
static void
wait_till_seen(int ipl)
{
int cpu_in_chain, cix;
do {
cpu_in_chain = 0;
cpu_in_chain = 1;
} else {
}
}
}
} while (cpu_in_chain);
}
static uint64_t dummy_tick;
/* remove an interrupt vector from the chain */
static void
int vect)
{
int ipl;
/*
* Protect rewrites of the list
*/
hi_pri = 0;
/* found the handler */
target = p;
continue;
}
if (p->av_prilevel > hi_pri)
hi_pri = p->av_prilevel;
if (p->av_prilevel < lo_pri)
lo_pri = p->av_prilevel;
}
}
printf("Couldn't remove function %p at %d, %d\n",
return;
}
/*
* This drops the handler from the chain, it can no longer be called.
* However, there is no guarantee that the handler is not currently
* still executing.
*/
/*
* There is a race where we could be just about to pick up the ticksp
* pointer to increment it after returning from the service routine
* in av_dispatch_autovect. Rather than NULL it out let's just point
* it off to something safe so that any final tick update attempt
* won't fault.
*/
/* Leave the unused entries here for probable future use */
vectp->avh_hi_pri = 0;
} else {
}
}
/*
* kmdb uses siron (and thus setsoftint) while the world is stopped in order to
* inform its driver component that there's work to be done. We need to keep
* DTrace from instrumenting kmdb's siron and setsoftint. We duplicate siron,
* giving kmdb's version a kdi prefix to keep DTrace at bay. We also
* provide a version of the various setsoftint functions available for kmdb to
* use using a kdi_ prefix while the main *setsoftint() functionality is
* implemented as a wrapper. This allows tracing, while still providing a
* way for kmdb to sneak in unmolested.
*/
void
kdi_siron(void)
{
}
/*
* Trigger a soft interrupt.
*/
void
siron(void)
{
/* Level 1 software interrupt */
}
/*
* Trigger software interrupts dedicated to ddi timer.
*/
void
{
}
/*
* The handler which is executed on the target CPU.
*/
/*ARGSUSED*/
static int
{
siron();
return (0);
}
/*
* May get called from softcall to poke CPUs.
*/
void
{
/*
* If we are poking to ourself then we can simply
* generate level1 using siron()
*/
siron();
if (CPUSET_ISNULL(poke))
return;
}
}
/*
* Walk the autovector table for this vector, invoking each
* interrupt handler as we go.
*/
extern uint64_t intr_get_time(void);
void
{
uint_t r;
/*
* We must walk the entire chain. Removed handlers
* may be anywhere in the chain.
*/
continue;
numcalled++;
claimed |= r;
}
/*
* If there's only one interrupt handler in the chain,
* or if no-one claimed the interrupt at all give up now.
*/
break;
}
}
/*
* Call every soft interrupt handler we can find at this level once.
*/
void
{
/*
* We must walk the entire chain. Removed handlers
* may be anywhere in the chain.
*/
continue;
/*
* Each cpu has its own pending bit in hdlp->ih_pending,
* here av_check/clear_softint_pending is just checking
* and clearing the pending bit for the current cpu, who
* has just triggered a softint.
*/
}
}
}
struct regs;
/*
* Call every NMI handler we know of once.
*/
void
{
}