HDK Technical Reference

Context of a driver

The context of a driver defines the system state in which an entry point is called. Each entry point executes in one (and only one) context, which is identified on the Section D2 manual pages or Section D2oddi manual pages for the entry point routine. Functions may execute in more than one context, and the Section D3 manual pages and Section D3oddi manual pages identifies those contexts.

The context in which an entry point routine is called affects the types of operations the driver may perform:

Note that earlier releases discussed these distinctions as levels: interrupt level versus base, task, or program level.

The contexts from which driver code can execute are:

user context
Driver is called in the context of the user-level process that is requesting service. The driver can block and can do operations such as copyout(D3) that require access to the requesting process's user-level address space. The ioctl( ), read( ), and write( ) entry point routines execute in user context.

blockable context
Driver is called from the context of an arbitrary user-level process. The driver can block but cannot do operations such as copyout(D3) that require access to the requesting process's address space. Examples of entry point routines that execute in blockable context are biostart( ), strategy( ), open( ), close( ) config( ), _load( ), and _unload( ).

non-blockable context
Driver cannot block and cannot call functions such as copyout(D3) that access the address space of the initiating process. The start(D2) or start(D2oddi) entry point routine, used in DDI versions before DDI 8 and on SCO OpenServer 5 systems, executes in non-blockable context.

Acquiring a spin lock from user or blockable context in a DDI driver puts the driver into non-blockable context; when the lock is released, the context reverts to the original context. DDI sleep locks do not affect context. Drivers must not hold a spin lock when calling a function that could block such as delay(D3) or kmem_alloc(D3) with the KM_SLEEP flag set. See ``Spin locks (DDI)'' and ``Calling functions at wrong context''.

initialization context
The initialization context is like the non-blockable context, with additional restrictions because most system facilities are not available and interrupts are not enabled. The init(D2) or init(D2oddi) entry point routine, used in earlier DDI versions and on SCO OpenServer 5, executes in initialization context. Beginning with DDI 8, all driver initialization occurs in the _load(D2) entry point routine so drivers are never in the initialization context, even if they are configured statically with the $static flag in their System(DSP/4dsp) file.

interrupt context
Driver is called outside the normal flow of execution, such as in response to an interrupt or because of the expiration of a timer. It is a special instance of the non-blockable context, which is functionally equivalent in a multiprocessor-safe driver.

In uniprocessor drivers, only the interrupt context can execute simultaneously with other contexts. This imposes special limits about priority levels used with locks and spl( ) calls. The intr(D2) or intr(D2oddi) interrupt handler entry point routine executes in interrupt context, as do asynchronous callback routines scheduled through itimeout(D3), bufcall(D3str), and related functions. STREAMS service procedures also execute in interrupt context. The driver may not block or access any process state information while running in interrupt context.

The following table summarizes the characteristics of each context:

                ||Can    | User     | In    |
 Context        ||block? | context? | flow? | Other
 User           ||Yes    | Yes      | Yes   |
 Blockable      ||Yes    |          | Yes   |
 Non-blockable  ||       |          | Yes   |
 Interrupt      ||       |          | No    |
 Initialization ||       |          | Yes   | Interrupts not enabled

If a function requires blockable context, the driver must ensure that it is called only from the blockable context. This is guaranteed if the code is called from entry point routines that execute in blockable (or user) context such as ioctl( ), biostart( ), read( ), write( ), and strategy( ).

For DDI 8 drivers, if the driver is being run from kernel context (such as in response to an interrupt), all calls to blockable functions must be run on a thread that the driver creates. DDI 8 drivers use the kthread_spawn(D3) function to execute a kernel function that is guaranteed to run in blockable context.

The model is to have the kthread-spawn'ed function blocked on a synchronization variable that the driver sets in response to some kernel event such as queuing the result of an interrupt. The following pseudo-code illustrates this:

   mydrv_init() {

myfunc(args) { SV_WAIT(mysvp); drv_open(otherdrv) /* do other stuff */ /* back to SV_WAIT() */ }

myintr(){ LOCK(<some stuff>) /* put otherdrv data someplace safe */ UNLOCK SV_BROADCAST(mysvp); }

Note that you never have user context when running in a kernel thread. Even if you spawn the thread from an entry point routine that has user context, the kernel thread does not have user context.
© 2005 The SCO Group, Inc. All rights reserved.
OpenServer 6 and UnixWare (SVR5) HDK - June 2005