Monday July 11, 2005 | Anish's Weblog Anish's Weblog |
|
Solaris Express 6/05 announces new DDI interrupt interfaces Check out What's new in Solaris Express 6/05. Posted by anish ( Jul 11 2005, 01:52:50 PM PDT ) Permalink Comments [1] Device Driver Development: How to add interrupts in a driver New DDI interrupt interfacesThis writeup is about the new DDI interrupt interfaces introduced in OpenSolaris.The older DDI interrupt interfaces are being replaced for the following reasons:
IMPORTANT: See Advanced Interrupt Handlers added recently to developer.sun.com that replaced most of this blog. Topics not covered there are still retained here. New DDI Interrupt Data structures
See OpenSolaris source code
that
defines data structures to be used with the new DDI interrupt
framework: |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Legacy Interrupt Functions | Replacements (Recommended) |
|---|---|
|
ddi_add_intr(9F) |
Three-step process:
|
| ddi_add_softintr(9F) | ddi_intr_add_softint(9F) |
| ddi_dev_nintrs(9F) | ddi_intr_get_nintrs(9F) |
| ddi_get_iblock_cookie(9F) | Three-step process:
|
| ddi_get_soft_iblock_cookie(9F) | Three-step process:
|
| ddi_intr_hilevel(9F) | ddi_intr_get_hilevel_pri(9F) |
| ddi_remove_intr(9F) | ddi_intr_remove_handler(9F) |
| ddi_remove_softintr(9F) | ddi_intr_remove_softint(9F) |
| ddi_trigger_softintr(9F) | ddi_intr_trigger_softint(9F) |
| ddi_idevice_cookie(9S) | Not applicable |
| ddi_iblock_cookie(9S) for mutex_init(9F) etc. |
(void *)(uintptr_t) |
/* Change the soft interrupt priority to 9 */
if (ddi_intr_set_softint_pri(mydev->mydev_softint_hdl, 9) !=
DDI_SUCCESS) {
cmn_err (CE_WARN, "ddi_intr_set_softint_pri failed");
}
/* Check if an interrupt is pending */
if (ddi_intr_get_pending(mydevp->htable[0], &pending) != DDI_SUCCESS) {
cmn_err(CE_WARN, "ddi_intr_get_pending() failed");
} else if (pending)
cmn_err(CE_NOTE, "ddi_intr_get_pending(): Interrupt pending");
/* Set interrupt masking to prevent device from receiving interrupts */
if ((ddi_intr_set_mask(mydevp->htable[0]) != DDI_SUCCESS))
cmn_err(CE_WARN, "ddi_intr_set_mask() failed");
/* Clear interrupt masking. If successful device will start generating interrupts */
if (ddi_intr_clr_mask(mydevp->htable[0]) != DDI_SUCCESS)
cmn_err(CE_WARN, "ddi_intr_clr_mask() failed");
Hardware interrupts overview for Solaris X86
Welcome to OpenSolaris, and the wonders of Solaris 10.
This paper provides a brief introduction to hardware interrupts on x86 platforms. It is relevant for Intel and AMD based platforms. Interrupt handling is done via interrupt controller hardware in the system which are mostly sideband signals. Inband interrupts, for e.g. Message Signalled Interrupts (MSIs), introduced with PCI v2.2 specification onwards, will be discussed in another blog. MSIs are becoming mainstay with advent of new interrconnects like PCI-Express.
However, there are mainly two kinds of hardware interrupt controllers which are commonly used on x86 platforms:
This is supported by the Solaris uppc(7d) module and its source is located at usr/src/uts/i86pc/io/psm/uppc.c. Each PIC can handle 8 vectored priority interrupting sources and there are two PICs cascaded together to provide 16 interrupts on x86 systems. However, one of the pin - IRQ2 of the 1st PIC is used to cascade the 2nd PIC and so there are only 15 interrupt sources. This can not be used for multiprocessor (MP) systems without any major modifications.
This is supported by the Solaris pcplusmp(7d) module and its source is located at usr/src/uts/i86pc/io/pcplusmp/apic.c. It consists of two components - I/O APIC and Local APIC. The Local APIC is embedded in the CPU while the I/O APIC is used for connecting the interrupting sources. The Local APIC also has the capability to send interprocessor interrupt from one cpu to another and so APIC is widely used on all the x86 MP systems. Each system can have multiple I/O APICs and each I/O APIC can have 4, 16, 20 or 24 interrupt pins. Since the Local APIC is embedded in the CPU and the I/O APIC can handle more than 16 interrupt sources, even the single-CPU systems uses APIC as well instead of some other hardware.
There are many systems, which have I/O APICs with 4 inputs(this is typically done for PCI-X slotted systems, where each slot is given a dedicated I/O APIC, enabling INTA-INTD for each of the slots to have a dedicated input).
Solaris supports multiple I/O APICs.
processor #1 processor #2
+-----------+ +-----------+
| | | |
| CPU | | CPU |
| | | |
+-----------+ +-----------+
| local APIC| | local APIC|
+-----------+ +-----------+
^ ^
| |
| |
| |
v v Processor system bus
<----------------------------------------------------------------->
^
|
|
|
+-----------------------------+
| | |
| v |
| +--------+ |
| | | |
| | Bridge | |
| | | |
| +--------+ |
| ^ |
| | |
| v PCI |
| <------------------> |
| ^ |
| | |
| v |
| +----------+ |
| | | |
| | I/O APIC |<-----|---External
| | |<-----|---Interrupts
| +----------+ |
| |
+-----------------------------+
System Chip set
When a device driver adding the interrupt through ddi_add_intr(9f),
it
eventually gets to uppc_addspl() in uppc(7d) for PIC or apic_addspl() in pcplusmp(7d) if using APIC. The interrupt pin will be identified and then enabled. It is quite simple for the uppc(7d) case, the interrupt pin to enable on the PICs is basically 1-1 mapped to the "IRQ#" or the
"interrupt" property of the device on Solaris. But for APIC (pcplusmp(7d)), it is a lot more complicated as internally it either
uses the MP Spec. 1.4[2] or ACPI specification[3] to locate the right interrupt pin of the right I/O APIC for the device. The system BIOS sets up how the interrupts are routed and saves that information in either the MP Specification table or somewhere that ACPI can easily access. pcplusmp(7d) then access that information to initialize and add the interrupts.
During the ddi_add_intr(9f) call, the device interrupt handler's entry point is stored in the autovect[] and the interrupt pin will be enabled through uppc_addspl (for PCI) or apic_addspl(for APIC). Also, before the interrupt pin is enabled, an interrupt "vector" (refered to as "vector" from now on) will have to be selected for the CPU to trigger when that particular interrupt comes in. For Intel CPUs, there are total 256 vectors and the first 32 vectors are reserved for special functions and so the first available vector for devices is 32 (or hex 0x20).
For uppc(7d), vectors are set up such that the 1st pin or IRQ#0 is mapped to 32, IRQ1 to 33 and
so on. As for pcplusmp(7d), it is not as simple. Solaris handles interrupts based on interrupt priority and each device is assigned a unique priority (can be modified by the device driver). Say, if a device "abc" is assigned priority 5, then all other interrupts at 5 or lower can NOT be triggered when the interrupt handler of "abc" is executing. However, an interrupt of priority 6 or higher is allowed to trigger. Since APIC has mechanism to prioritize interrupts, pcplusmp(7d) needs to select the vectors accordingly.
In order to handle the interrupt priority properly, there are few internal interface calls provided by uppc(7d) and pcplusmp(7d). They are the uppc_intr_enter()/uppc_intr_exit() for the uppc(7d); and apic_intr_enter()/apic_intr_exit() for pcplusmp(7d). After the interrupt is triggered but before the interrupt handler is called, uppc_intr_enter() or apic_intr_enter() will be called to setup the interrupt priority accordingly to block all other interrupts with the same or lower priority. After the interrupt handler is completed, then uppc_intr_exit() or apic_intr_exit() is called to restore the interrupt priority.
On the x86 platform, all the local variables of the interrupt handler are on stack. Also, if the interrupt handler needs to call another
function, the parameters that are passed to the function are on stack too. i.e. all the interrupt handlers should use the stack one way or
the other.
usr/src/uts/common/io/avintr.c
#define MAX_VECT 256
struct av_head autovect[MAX_VECT];
usr/src/uts/common/sys/avintr.h
struct autovec {
/*
* Interrupt handler and argument to pass to it.
*/
struct autovec *av_link; /* pointer to next on in chain */
uint_t (*av_vector)();
caddr_t av_intarg;
uint_t av_prilevel; /* priority level */
/*
* Interrupt handle/id (like intrspec structure pointer) used to
* identify a specific instance of interrupt handler in case we
* have to remove the interrupt handler later.
*
*/
void *av_intr_id;
dev_info_t *av_dip;
};
av_vector is the device interrupt handler.
struct av_head {
struct autovec *avh_link;
ushort_t avh_hi_pri;
ushort_t avh_lo_pri;
};
- All interrupts run at some priority which has a ceiling of LOCK_LEVEL.
Interrupts below LOCK_LEVEL run as threads.
usr/src/uts/intel/sys/machlock.h
#define CLOCK_LEVEL 10
#define LOCK_LEVEL 10
- The following sequence shows what is done for each CPU to allocate
enough interrupt threads to handle the interrupts. Since interrupts
are prioritized, one interrupt thread per priority should be sufficient.
usr/src/uts/i86pc/os/mp_startup.c
void
start_other_cpus(int cprboot)
{
for (who = 0; who < NCPU; who++) {
mp_startup_init(who);
...
}
void
mp_startup_init(void)
{
init_intr_threads(cp);
...
}
usr/src/uts/i86pc/os/intr.c
/*
* Allocate threads and stacks for interrupt handling.
*/
#define NINTR_THREADS (LOCK_LEVEL-1) /* number of interrupt threads */
void
init_intr_threads(struct cpu *cp)
{
int i;
for (i = 0; i < NINTR_THREADS; i++)
(void) thread_create_intr(cp);
...
}
usr/src/uts/common/disp/thread.c
void
thread_create_intr(struct cpu *cp)
{
- Here is the actual code handling the interrupts on x86.
*setlvl is the wrapper for uppc_intr_enter() or apic_intr_enter().
}
NOTE: Calling the interrupt handler is done in low level assembly code
which is not discussed here.
Device Type |
Attachment Type format |
| Port Devices |
ib::PORT_GUID,0,service-name |
| HCA_SVC devices |
ib::HCA_GUID,0,servicename |
| VPPA devices |
ib::PORT_GUID,P_Key,service-name |
| IOC devices |
ib::IOC-GUID |
| Pseudo devices |
ib::driver_name,unit-address |
Static attachment |
Attachment type format |
| IB Fabric |
ib |
| Host Channel Adapter(s) |
hca:HCA-GUID |
# cfgadm -a ib hca:2C90109764440 Ap_Id Type Receptacle Occupant Condition hca:2C90109764440 IB-HCA connected configured ok ib IB-Fabric connected configured ok ib::2C90109764440,0,svch IB-HCA_SVC connected unconfigured unknown ib::2C90109764441,0,psvc IB-PORT connected unconfigured unknown ib::2C90109764441,ffff,ipib IB-VPPA connected unconfigured unknown ib::2C90109764442,0,psvc IB-PORT connected unconfigured unknown ib::2C90109764442,ffff,ipib IB-VPPA connected unconfigured unknown ib::daplt,0 IB-PSEUDO connected configured ok ib::rpcib,0 IB-PSEUDO connected configured ok #
# cfgadm -x list_clients hca:2C90109764440 Ap_Id IB Client Alternate HCA ib::daplt,0 daplt no ib::rpcib,0 nfs/ib no - ibmf no - ibdm no #
# cfgadm -a ib::daplt,0 Ap_Id Type Receptacle Occupant Condition ib::daplt,0 IB-PSEUDO connected unconfigured unknown # cfgadm -yc configure ib::daplt,0 # cfgadm -a ib::daplt,0 Ap_Id Type Receptacle Occupant Condition ib::daplt,0 IB-PSEUDO connected configured ok #Unconfiguring a device:
# cfgadm -a ib::daplt,0 Ap_Id Type Receptacle Occupant Condition ib::daplt,0 IB-PSEUDO connected configured ok # cfgadm -yc unconfigure ib::daplt,0 # cfgadm -a ib::daplt,0 Ap_Id Type Receptacle Occupant Condition ib::daplt,0 IB-PSEUDO connected unconfigured unknownThe example below shows how to unconfigure all kernel clients of a give InfiniBand Host Channel Adapter.
# cfgadm -x unconfig_clients hca:2C90109764440 Unconfigure Clients of HCA /devices/ib:2C90109764440 This operation will unconfigure IB clients of this HCA Continue (yes/no)? yes <<<<<<< #
# cfgadm -x list_services ib
PORT communication services:
psvc
VPPA communication services:
ipib
HCA communication services:
svch
# cfgadm -o comm=port,service=srp -x add_service ib
# cfgadm -x list_services ib
PORT communication services:
srp <<<<<<<<<<<<<<
psvc
VPPA communication services:
ipib
HCA communication services:
svch
#
Delete a communication service:
# cfgadm -x list_services ib
PORT communication services:
srp
psvc
VPPA communication services:
ipib
HCA communication services:
svch
# cfgadm -o comm=port,service=srp -x delete_service ib
# cfgadm -x list_services ib
PORT communication services:
psvc
VPPA communication services:
ipib
HCA communication services:
svch
#
Note that the examples are shown only for Port Devices but are applicable to all three device types.
port-list, port-entries, service-id, and service-name
History of Jammu According to the most popular legend, Jammu city was founded by Raja Jamboolochan in 14th century BC as he found divine power here. During one of his hunting campaigns he reached near a river (Tawi) where he saw a goat and a lion drinking water at the same place. The king was impressed and decided to set up a town after his name, Jamboo. With the passage of time the name got corrupted and became "Jammu". The city name figures in the ancient book Mahabharata. Name of Jammu city is also found in the memories of Timur.Excavation near Akhnoor provide evidences that Jammu was a part of Harappan civiliaziation. Remains of Mauryan , Kushan, and Gupta periods have also been found. Posted by anish ( Jun 03 2005, 11:54:56 PM PDT ) Permalink Comments [2]
I am from Jammu
Tawi, the Indian city of temples. It is part of Indian state
of Jammu and Kashmir.
Every year millions of devotees pass through Jammu to visit Vaishno Devi
temple shrine. Last year more than 6 million devotees journeyed there.
The unique thing about it is that the actual temple is located at an
elevation 5200
feet. Devotees trek a total of 13 kilometer
(~8.1 miles) from an altitude of 2500 feet to reach the temple.
Hi !! Finally decided to join the rank and file of bloggers :-) I work on Solaris in the I/O group and have been w/ Sun for a while now. Worked on USB initially and am guilty of authoring scsa2usb(7d). Dabbled in InfiniBand development and am now working on MSI interrupts, PCI-Express.
Posted by anish ( May 27 2005, 02:10:49 PM PDT ) Permalink Comments [1]| Archives | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Language | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Links | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Referrers | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Today's Page Hits: 69 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||