summaryrefslogtreecommitdiff
path: root/usr/src/man/man9f/ddi_add_softintr.9f
blob: 84e58f6f53db6843100e82c1ec0f0f1ccd4496d8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
'\" te
.\" Copyright (c) 2005, Sun Microsystems, Inc.
.\" 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 or http://www.opensolaris.org/os/licensing.  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]
.TH DDI_ADD_SOFTINTR 9F "Oct 19, 2005"
.SH NAME
ddi_add_softintr, ddi_get_soft_iblock_cookie, ddi_remove_softintr,
ddi_trigger_softintr \- software interrupt handling routines
.SH SYNOPSIS
.nf
#include <sys/types.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>



\fBint\fR \fBddi_get_soft_iblock_cookie\fR(\fBdev_info_t *\fR\fIdip\fR,
\fBint\fR \fIpreference\fR, \fBddi_iblock_cookie_t *\fR\fIiblock_cookiep\fR);
.fi

.LP
.nf
\fBint\fR \fBddi_add_softintr\fR(\fBdev_info_t *\fR\fIdip\fR, \fBint\fR \fIpreference\fR, \fBddi_softintr_t *\fR\fIidp\fR,
     \fBddi_iblock_cookie_t *\fR\fIiblock_cookiep\fR, \fBddi_idevice_cookie_t *\fR
     \fIidevice_cookiep\fR,
     \fBuint_t(*\fR\fIint_handler\fR) (caddr_t \fIint_handler_arg\fR), \fBcaddr_t\fR
     \fIint_handler_arg\fR);
.fi

.LP
.nf
\fBvoid\fR \fBddi_remove_softintr\fR(\fBddi_softintr_t\fR \fIid\fR);
.fi

.LP
.nf
\fBvoid\fR \fBddi_trigger_softintr\fR(\fBddi_softintr_t\fR \fIid\fR);
.fi

.SH INTERFACE LEVEL
illumos DDI specific (illumos DDI). These interfaces are obsolete. Use the new
interrupt interfaces referenced in \fBIntro\fR(9F). Refer to \fIWriting Device
Drivers\fR for more information.
.SH PARAMETERS
\fBddi_get_soft_iblock_cookie()\fR
.sp
.ne 2
.na
\fB\fIdip\fR\fR
.ad
.RS 18n
Pointer to a \fBdev_info\fR structure.
.RE

.sp
.ne 2
.na
\fB\fIpreference\fR\fR
.ad
.RS 18n
The type of soft interrupt to retrieve the cookie for.
.RE

.sp
.ne 2
.na
\fB\fIiblock_cookiep\fR\fR
.ad
.RS 18n
Pointer to a location to store the interrupt block cookie.
.RE

.sp
.LP
\fBddi_add_softintr()\fR
.sp
.ne 2
.na
\fB\fIdip\fR\fR
.ad
.RS 19n
Pointer to \fBdev_info\fR structure.
.RE

.sp
.ne 2
.na
\fB\fIpreference\fR\fR
.ad
.RS 19n
A hint value describing the type of soft interrupt to generate.
.RE

.sp
.ne 2
.na
\fB\fIidp\fR\fR
.ad
.RS 19n
Pointer to a soft interrupt identifier where a returned soft interrupt
identifier is stored.
.RE

.sp
.ne 2
.na
\fB\fIiblock_cookiep\fR\fR
.ad
.RS 19n
Optional pointer to an interrupt block cookie where a returned interrupt block
cookie is stored.
.RE

.sp
.ne 2
.na
\fB\fIidevice_cookiep\fR\fR
.ad
.RS 19n
Optional pointer to an interrupt device cookie where a returned interrupt
device cookie is stored (not used).
.RE

.sp
.ne 2
.na
\fB\fIint_handler\fR\fR
.ad
.RS 19n
Pointer to interrupt handler.
.RE

.sp
.ne 2
.na
\fB\fIint_handler_arg\fR\fR
.ad
.RS 19n
Argument for interrupt handler.
.RE

.sp
.LP
\fBddi_remove_softintr()\fR
.sp
.ne 2
.na
\fB\fIid\fR\fR
.ad
.RS 6n
The identifier specifying which soft interrupt handler to remove.
.RE

.sp
.LP
\fBddi_trigger_softintr()\fR
.sp
.ne 2
.na
\fB\fIid\fR\fR
.ad
.RS 6n
The identifier specifying which soft interrupt to trigger and which soft
interrupt handler will be called.
.RE

.SH DESCRIPTION
For \fBddi_get_soft_iblock_cookie()\fR:
.sp
.LP
\fBddi_get_soft_iblock_cookie()\fR retrieves the interrupt block cookie
associated with a particular soft interrupt preference level. This routine
should be called before \fBddi_add_softintr()\fR to retrieve the interrupt
block cookie needed to initialize locks ( \fBmutex\fR(9F), \fBrwlock\fR(9F))
used by the software interrupt routine. \fIpreference\fR determines which type
of soft interrupt to retrieve the cookie for. The possible values for
\fIpreference\fR are:
.sp
.ne 2
.na
\fB\fBDDI_SOFTINT_LOW\fR\fR
.ad
.RS 20n
Low priority soft interrupt.
.RE

.sp
.ne 2
.na
\fB\fBDDI_SOFTINT_MED\fR\fR
.ad
.RS 20n
Medium priority soft interrupt.
.RE

.sp
.ne 2
.na
\fB\fBDDI_SOFTINT_HIGH\fR\fR
.ad
.RS 20n
High priority soft interrupt.
.RE

.sp
.LP
On a successful return, \fIiblock_cookiep\fR contains information needed for
initializing locks associated with this soft interrupt (see
\fBmutex_init\fR(9F) and \fBrw_init\fR(9F)). The driver can then initialize
mutexes acquired by the interrupt routine before calling
\fBddi_add_softintr()\fR which prevents a possible race condition where the
driver's soft interrupt handler is called immediately \fBafter\fR the driver
has called \fBddi_add_softintr()\fR but \fBbefore\fR the driver has initialized
the mutexes. This can happen when a soft interrupt for a different device
occurs on the same soft interrupt priority level. If the soft interrupt routine
acquires the mutex before it has been initialized, undefined behavior may
result.
.sp
.LP
For \fBddi_add_softintr()\fR:
.sp
.LP
\fBddi_add_softintr()\fR adds a soft interrupt to the system. The user
specified hint \fIpreference\fR identifies three suggested levels for the
system to attempt to allocate the soft interrupt priority at. The value for
\fIpreference\fR should be the same as that used in the corresponding call to
\fBddi_get_soft_iblock_cookie()\fR. Refer to the description of
\fBddi_get_soft_iblock_cookie()\fR above.
.sp
.LP
The value returned in the location pointed at by \fIidp\fR is the soft
interrupt identifier. This value is used in later calls to
\fBddi_remove_softintr()\fR and \fBddi_trigger_softintr()\fR to identify the
soft interrupt and the soft interrupt handler.
.sp
.LP
The value returned in the location pointed at by \fIiblock_cookiep\fR is an
interrupt block cookie which contains information used for initializing mutexes
associated with this soft interrupt (see \fBmutex_init\fR(9F) and
\fBrw_init\fR(9F)). Note that the interrupt block cookie is normally obtained
using \fBddi_get_soft_iblock_cookie()\fR to avoid the race conditions described
above (refer to the description of \fBddi_get_soft_iblock_cookie()\fR above).
For this reason, \fIiblock_cookiep\fR is no longer useful and should be set to
\fINULL\fR.
.sp
.LP
\fIidevice_cookiep\fR is not used and should be set to \fINULL\fR.
.sp
.LP
The routine \fIint_handler\fR, with its argument \fIint_handler_arg\fR, is
called upon receipt of a software interrupt. Software interrupt handlers must
not assume that they have work to do when they run, since (like hardware
interrupt handlers) they may run because a soft interrupt occurred for some
other reason. For example, another driver may have triggered a soft interrupt
at the same level. For this reason, before triggering the soft interrupt, the
driver must indicate to its soft interrupt handler that it should do work. This
is usually done by setting a flag in the state structure. The routine
\fIint_handler\fR checks this flag, reachable through \fIint_handler_arg\fR, to
determine if it should claim the interrupt and do its work.
.sp
.LP
The interrupt handler must return \fBDDI_INTR_CLAIMED\fR if the interrupt was
claimed, \fBDDI_INTR_UNCLAIMED\fR otherwise.
.sp
.LP
If successful, \fBddi_add_softintr()\fR will return \fBDDI_SUCCESS\fR; if the
interrupt information cannot be found, it will return \fBDDI_FAILURE\fR.
.sp
.LP
For \fBddi_remove_softintr()\fR:
.sp
.LP
\fBddi_remove_softintr()\fR removes a soft interrupt from the system. The soft
interrupt identifier \fIid\fR, which was returned from a call to
\fBddi_add_softintr()\fR, is used to determine which soft interrupt and which
soft interrupt handler to remove. Drivers must remove any soft interrupt
handlers before allowing the system to unload the driver.
.sp
.LP
For \fBddi_trigger_softintr()\fR:
.sp
.LP
\fBddi_trigger_softintr()\fR triggers a soft interrupt. The soft interrupt
identifier \fIid\fR is used to determine which soft interrupt to trigger. This
function is used by device drivers when they wish to trigger a soft interrupt
which has been set up using \fBddi_add_softintr()\fR.
.SH RETURN VALUES
\fBddi_add_softintr()\fR and \fBddi_get_soft_iblock_cookie()\fR return:
.sp
.ne 2
.na
\fB\fBDDI_SUCCESS\fR\fR
.ad
.RS 15n
on success
.RE

.sp
.ne 2
.na
\fB\fBDDI_FAILURE\fR\fR
.ad
.RS 15n
on failure
.RE

.SH CONTEXT
These functions can be called from user or kernel context.
\fBddi_trigger_softintr()\fR may be called from high-level interrupt context as
well.
.SH EXAMPLES
\fBExample 1 \fRdevice using high-level interrupts
.sp
.LP
In the following example, the device uses high-level interrupts. High-level
interrupts are those that interrupt at the level of the scheduler and above.
High level interrupts must be handled without using system services that
manipulate thread or process states, because these interrupts are not blocked
by the scheduler. In addition, high level interrupt handlers must take care to
do a minimum of work because they are not preemptable. See
\fBddi_intr_hilevel\fR(9F).

.sp
.LP
In the example, the high-level interrupt routine minimally services the device,
and enqueues the data for later processing by the soft interrupt handler. If
the soft interrupt handler is not currently running, the high-level interrupt
routine triggers a soft interrupt so the soft interrupt handler can process the
data. Once running, the soft interrupt handler processes all the enqueued data
before returning.

.sp
.LP
The state structure contains two mutexes. The high-level mutex is used to
protect data shared between the high-level interrupt handler and the soft
interrupt handler. The low-level mutex is used to protect the rest of the
driver from the soft interrupt handler.

.sp
.in +2
.nf
struct xxstate {
      .\|.\|.
      ddi_softintr_t             id;
         ddi_iblock_cookie_t     high_iblock_cookie;
         kmutex_t                      high_mutex;
         ddi_iblock_cookie_t     low_iblock_cookie;
         kmutex_t                      low_mutex;
         int                              softint_running;
      .\|.\|.
};
struct xxstate *xsp;
static uint_t xxsoftintr(caddr_t);
static uint_t xxhighintr(caddr_t);
\&.\|.\|.
.fi
.in -2

.LP
\fBExample 2 \fRsample \fBattach()\fR routine
.sp
.LP
The following code fragment would usually appear in the driver's
\fBattach\fR(9E) routine. \fBddi_add_intr\fR(9F) is used to add the high-level
interrupt handler and \fBddi_add_softintr()\fR is used to add the low-level
interrupt routine.

.sp
.in +2
.nf
static uint_t
xxattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
         struct xxstate *xsp;
         .\|.\|.
      /* get high-level iblock cookie */
         if (ddi_get_iblock_cookie(dip, \fIinumber\fR,
                &xsp->high_iblock_cookie) != DDI_SUCCESS)  {
                      /* clean up */
                      return (DDI_FAILURE); /* fail attach */
         }

         /* initialize high-level mutex */
         mutex_init(&xsp->high_mutex, "xx high mutex", MUTEX_DRIVER,
               (void *)xsp->high_iblock_cookie);

         /* add high-level routine - xxhighintr() */
         if (ddi_add_intr(dip, \fIinumber\fR, NULL, NULL,
                xxhighintr, (caddr_t) xsp) != DDI_SUCCESS)  {
                      /* cleanup */
                      return (DDI_FAILURE); /* fail attach */
         }

         /* get soft iblock cookie */
         if (ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_MED,
                &xsp->low_iblock_cookie) != DDI_SUCCESS)  {
                      /* clean up */
                      return (DDI_FAILURE); /* fail attach */
         }

         /* initialize low-level mutex */
         mutex_init(&xsp->low_mutex, "xx low mutex", MUTEX_DRIVER,
                (void *)xsp->low_iblock_cookie);

         /* add low level routine - xxsoftintr() */
         if ( ddi_add_softintr(dip, DDI_SOFTINT_MED, &xsp->id,
                NULL, NULL, xxsoftintr, (caddr_t) xsp) != DDI_SUCCESS) {
                      /* cleanup */
                      return (DDI_FAILURE);  /* fail attach */
         }

         .\|.\|.
}
.fi
.in -2

.LP
\fBExample 3 \fRHigh-level interrupt routine
.sp
.LP
The next code fragment represents the high-level interrupt routine. The
high-level interrupt routine minimally services the device, and enqueues the
data for later processing by the soft interrupt routine. If the soft interrupt
routine is not already running, \fBddi_trigger_softintr()\fR is called to start
the routine. The soft interrupt routine will run until there is no more data on
the queue.

.sp
.in +2
.nf
static uint_t
xxhighintr(caddr_t arg)
{
      struct xxstate *xsp = (struct xxstate *) arg;
         int need_softint;
         .\|.\|.
         mutex_enter(&xsp->high_mutex);
         /*
         * Verify this device generated the interrupt
         * and disable the device interrupt.
         * Enqueue data for xxsoftintr() processing.
         */

         /* is xxsoftintr() already running ? */
         if (xsp->softint_running)
                need_softint = 0;
          else
                need_softint = 1;
          mutex_exit(&xsp->high_mutex);

          /* read-only access to xsp->id, no mutex needed */
          if (need_softint)
                ddi_trigger_softintr(xsp->id);
          .\|.\|.
          return (DDI_INTR_CLAIMED);
}

static uint_t
xxsoftintr(caddr_t arg)
{
      struct xxstate *xsp = (struct xxstate *) arg;
      .\|.\|.
         mutex_enter(&xsp->low_mutex);
      mutex_enter(&xsp->high_mutex);

      /* verify there is work to do */
      if (\fBwork queue empty\fR || xsp->softint_running )  {
                mutex_exit(&xsp->high_mutex);
                mutex_exit(&xsp->low_mutex);
                return (DDI_INTR_UNCLAIMED);
      }

      xsp->softint_running = 1;

         while ( \fBdata on queue\fR )  {
                ASSERT(mutex_owned(&xsp->high_mutex));

                /* de-queue data */

                mutex_exit(&xsp->high_mutex);

                /* Process data on queue */

                mutex_enter(&xsp->high_mutex);
          }

          xsp->softint_running = 0;
          mutex_exit(&xsp->high_mutex);
          mutex_exit(&xsp->low_mutex);

          return (DDI_INTR_CLAIMED);
}
.fi
.in -2

.SH ATTRIBUTES
See \fBattributes\fR(7) for descriptions of the following attributes:
.sp

.sp
.TS
box;
c | c
l | l .
ATTRIBUTE TYPE	ATTRIBUTE VALUE
_
Interface Stability	Obsolete
.TE

.SH SEE ALSO
.BR Intro (9F),
.BR ddi_add_intr (9F),
.BR ddi_in_panic (9F),
.BR ddi_intr_hilevel (9F),
.BR ddi_remove_intr (9F),
.BR mutex_init (9F)
.sp
.LP
\fIWriting Device Drivers\fR
.SH NOTES
\fBddi_add_softintr()\fR may not be used to add the same software interrupt
handler more than once. This is true even if a different value is used for
\fIint_handler_arg\fR in each of the calls to \fBddi_add_softintr()\fR.
Instead, the argument passed to the interrupt handler should indicate what
service(s) the interrupt handler should perform. For example, the argument
could be a pointer to the device's soft state structure, which could contain
a 'which_service' field that the handler examines. The driver must set this field
to the appropriate value before calling \fBddi_trigger_softintr()\fR.