|
|
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:
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''.
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() { SVALLOC(mysvp); kthread_spawn(myfunc,...) }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.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); }