NOTE: XXX To detect interrupts (for user-level probe routines) we must
allow for alloc without a pre-specified number. Such method
should allocate and return the next unhandled interrupt.
The kernel handles itself the clock interrupt to implement run queues
in processors. To re-export the clock interrupt to system users, there
are a predefined number of timers which can be considered to be
virtual clock interrupts. These timers are allocated and deallocated
by asking for their corresponding events to the interrupt table. The
machine dependent interrupt table reserves up to MDEP_NVINT_MAX
entries for fake virtual interrupts corresponding to these timers.
To implement timer interrupts the interrupt table only needs to allocate and deallocate them. Such interrupts are raised by the kernel on response to timer requests.
A new method of interrupt tables return the identifier of the first available virtual interrupt.
<Other public methods of off_IntTbl. >+= (<-U) [<-D->]
inline off_ev_id_t get_vint0(void) const;
<off_IntTbl::get_vint0 implementation. >= (U->)
inline off_ev_id_t off_IntTbl::get_vint0(void) const
{
off_ev_id_t i(get_id(),OFF_MDEP_VINT0_IRQ);
return i;
}
Where MDEP_VINT0_IRQ is the number of the first virtual
interrupt.
The same is done for the ``well-known'' timer interrupt.
<Other public methods of off_IntTbl. >+= (<-U) [<-D->]
inline off_ev_id_t get_clk(void) const;
<off_IntTbl::get_clk implementation. >= (U->)
inline off_ev_id_t off_IntTbl::get_clk(void) const
{
off_ev_id_t i(get_id(),OFF_MDEP_CLOCK_IRQ);
return i;
}
\subsubsection{Interrupt masking} Interrupts can be masked and acknowledged.
<Other public methods of off_Irq. >+= (<-U) [<-D->]
// Masks the interrupt.
void mask(void);
// Unmasks the interrupt.
void unmask(void);
// Is this interrupt masked?
boolean_t is_masked(void) const;
<Off event dependencies. >+= (U->) [<-D->] #include <flux/types.h> // for boolean_t natural_t et al. #include <klib/err.h> // for err_t
Interrupt masks are handled by the event table, where they can be implemented more efficiently.
<off_Irq::mask implementation. >= (U->)
// Masks the interrupt.
void off_Irq::mask(void)
{
((off_IntTbl*)(get_container()))->mask(this);
}
<off_Irq::unmask implementation. >= (U->)
// Masks the interrupt.
void off_Irq::unmask(void)
{
((off_IntTbl*)(get_container()))->unmask(this);
}
<off_Irq::is_masked implementation. >= (U->)
// Is this interrupt masked?
boolean_t off_Irq::is_masked(void) const
{
return ((off_IntTbl*)(get_container()))->is_masked(this);
}
We have thus new methods of IntTbl.
<Other public methods of off_IntTbl. >+= (<-U) [<-D->]
// Masks the interrupt.
void off_IntTbl::mask(off_Irq *i);
// Unmasks the interrupt.
void off_IntTbl::unmask(off_Irq *i);
// Is this interrupt masked?
boolean_t off_IntTbl::is_masked(const off_Irq *i) const;
They simply forward the request to the machine dependent part, where an interrupt mask is usually implemented as a word-sized bitmap.
<off_IntTbl::maskunmaskandis_maskedimplementation. >= (U->) // Masks the interrupt. inline void off_IntTbl::mask(off_Irq *i) { i_mdep.mask(*i); } // Unmasks the interrupt. inline void off_IntTbl::unmask(off_Irq *i) { i_mdep.unmask(*i); } // Is this interrupt masked? inline boolean_t off_IntTbl::is_masked(const off_Irq *i) const { return i_mdep.is_masked(*i); }
As we have seen, a machine dependent interrupt table is stored in
i_mdep.
<Other private members of off_IntTbl. >+= (<-U) [<-D->]
off_mdepIntTbl i_mdep; // Machine dependent interrupt table.
\subsubsection{Interrupt priorities}
Whenever an interrupt happens, we must choose either to evict the
current shuttle to dispatch the interrupt to its handler or to defer
interrupt delivering in favor of the current shuttle. To make a
choice, both shuttles and interrupts have priority-levels (see
section
for a discussion of interrupt priorities).
<Other public methods of off_Irq. >+= (<-U) [<-D->]
// Sets/Gets the priority level for this interrupt.
void set_prty(const off_pl_t prty);
off_pl_t get_prty(void) const;
<Off interrupt priority level data type. >= (U->) typedef unsigned8_t off_pl_t;
Definesoff_pl_t(links are to index).
Each interrupt contains its current priority,
<Other private members of off_Irq. >= (<-U) [D->]
off_pl_t i_pl; // priority level.
which is set by default to a value lower (i.e. higher priority) than that used by default in shuttles.
<Initialize other aggregate members of off_Irq. >= (<-U) [D->]
i_pl(OFF_NPL_IDFL),
<off_Irq::set_prty implementation. >= (U->)
// Sets/Gets the priority level for this interrupt.
void off_Irq::set_prty(const off_pl_t prty)
{
i_pl = prty;
}
<off_Irq::get_prty implementation. >= (U->)
off_pl_t off_Irq::get_prty(void) const
{
return i_pl;
}
On interrupt, we defer raising when the interrupt priority is lower (higher value) than that of the current shuttle.
<Ensure this interrupt can be raised. >= (<-U)
#if 0 // XXX fix it
if (get_prty() >= off_Shtl::self().get_prty()){
defer();
return EOK;
}
#endif
Where defer will record the current interrupt as pending and
return immediately.
<Other protected methods of off_Irq. >= (<-U) [D->]
// Deferred delivering.
void defer(void);
A count of the number of deferred interrupts is used.
<Other private members of off_Irq. >+= (<-U) [<-D->]
natural_t i_pending; // Pending count
<Initialize other aggregate members of off_Irq. >+= (<-U) [<-D]
i_pending(0)
After adjusting it we tell the interrupt table that a new interrupt has been deferred.
<off_Irq::defer implementation. >= (U->)
// Set deferred delivering.
void off_Irq::defer(void)
{
if (! i_pending++)
((off_IntTbl*)(get_container()))->defer(this);
}
When the processor priority level gets low enough for the interrupt to
be delivered, deferred will complete delivering.
<Other public methods of off_Irq. >+= (<-U) [<-D]
// Deferred delivering.
void deferred(void);
The interrupt error code (which will usually be zero) is set to the number of deferred interrupts.
<off_Irq::deferred implementation. >= (U->)
// Deferred delivering.
inline void off_Irq::deferred(void)
{
natural_t count=i_pending;
while(count--)
raise();
}
\subsubsection{Deferred interrupt delivering}
Interrupt tables implement defer to set a deferred interrupt and
also spl to be notified of priority level changes at the processor
affected.
<Other public methods of off_IntTbl. >+= (<-U) [<-D->]
// Sets an interrupt for deferred delivering.
void defer(off_Irq *i);
// Priority level changed.
void spl(off_pl_t pl);
We use a per-priority-level mask similar to the machine dependent interrupt mask for deferred interrupts.
<Other private members of off_IntTbl. >+= (<-U) [<-D->]
off_mdepIrqMask i_pending[OFF_NPL_MAX];
<Off limits. >= [D->] const int OFF_NPL_MAX = 32; // Max. number of interrupt priorities.
DefinesOFF_NPL_MAX(links are to index).
<Off event table dependencies. >+= (U->) [<-D->] #include <klib/limits.h> // for OFF_NPL_MAX
The default priority level used in shuttles is the middle one so that by default shuttles yield to interrupts.
<Off default priority levels. >= (U->) const int OFF_NPL_DFL = 16; // Default priority level for shuttles. const int OFF_NPL_IDFL= 8; // Default priority level for interrupts.
The implementation of defer simply marks the interrupt as
pending.
<off_IntTbl::defer implementation. >= (U->)
// Sets an interrupt for deferred delivering.
inline void off_IntTbl::defer(off_Irq *i)
{
i_pending[i->get_prty()].mask(*i);
}
Those queues are scanned on priority changes to deliver pending interrupts.
<off_IntTbl::spl implementation. >= (U->)
// Priority level changed.
void off_IntTbl::spl(off_pl_t pl)
{
for (off_pl_t p=i_pl; p<pl; p++)
while (!i_pending[p].is_empty()) {
for (natural_t i=0; i < OFF_MDEP_NINT_MAX; i++)
if (i_pending[p].is_masked(i)){
i_pending[p].unmask(i);
(*this+i)->deferred();
}
}
i_pl=pl;
}
We only had to scan the range from the old priority level (kept in
i_pl) to the new one.
<Other private members of off_IntTbl. >+= (<-U) [<-D->]
off_pl_t i_pl; // Current pl
<Initialize other aggregate members of off_IntTbl. >= (<-U) [D->]
i_pl(0),
\subsection{Event navigation and inspection}