bufcall(D3) bufcall(D3)
NAME
bufcall - call a function when a buffer becomes available
SYNOPSIS
#include <sys/types.h>
#include <sys/stream.h>
#include <sys/ddi.h>
toid_t bufcall(uint_t size, int pri, void (*func)(), long arg);
Arguments
size
Number of bytes in the buffer to be allocated (from the
failed allocb(D3) request).
pri
Priority of the allocb allocation request.
func
Function or driver routine to be called when a buffer
becomes available.
arg
Argument to the function to be called when a buffer becomes
available.
DESCRIPTION
When a buffer allocation request fails, the function bufcall
can be used to schedule the routine, func, to be called with
the argument, arg, when a buffer of at least size bytes
becomes available. bufcall serves, in effect, as a timeout
call of indeterminate length.
Return Values
On success, bufcall returns a non-zero value that identifies
the scheduling request. On failure, bufcall returns 0.
USAGE
When func runs, all interrupts from STREAMS devices will be
blocked. On multiprocessor systems, when func runs all
interrupts from STREAMS devices will be blocked on the
processor on which func is running. func will have no user
context and may not call any function that sleeps.
Even when func is called, allocb can still fail if another
module or driver had allocated the memory before func was able
to call allocb.
Copyright 1994 Novell, Inc. Page 1
bufcall(D3) bufcall(D3)
The pri argument is a hint to the allocator indicating how
badly the buffer is needed. BPRI_LO should be used for normal
data allocations. BPRI_MED should be used for other non-
critical allocations. BPRI_HI should be used for allocations
that absolutely must succeed, although success is not
guaranteed. Some implementations may choose to ignore this
argument.
The non-zero identifier returned by bufcall may be passed to
unbufcall(D3) to cancel the request.
Level
Base or Interrupt.
Synchronization Constraints
Does not sleep.
Driver-defined basic locks, read/write locks, and sleep locks
may be held across calls to this function.
Singlethreaded Example
The purpose of this service routine [see srv(D2)] is to add a
header to all M_DATA messages. We assume only M_DATA messages
are added to its queue. Service routines must process all
messages on their queues before returning, or arrange to be
rescheduled.
While there are messages to be processed (line 19), we check
to see if we can send the message on in the stream. If not,
we put the message back on the queue (line 21) and return.
The STREAMS flow control mechanism will re-enable us later
when messages can be sent. If canput(D3) succeeded, we try to
allocate a buffer large enough to hold the header (line 24).
If no buffer is available, the service routine must be
rescheduled later, when a buffer is available. We put the
original message back on the queue (line 26) and use bufcall
to attempt the rescheduling (lines 27 and 28). If bufcall
succeeds, we set the m_type field in the module's private data
structure to BUFCALL. If bufcall failed, we use timeout(D3)
to reschedule us instead (line 30). modcall will be called in
about a half second [drv_usectohz(500000)]. When the
rescheduling has been done, we return.
When modcall runs, it will set the m_type field to zero,
indicating that there is no outstanding request. Then the
queue's service routine is scheduled to run by calling
Copyright 1994 Novell, Inc. Page 2
bufcall(D3) bufcall(D3)
qenable(D3).
If the buffer allocation is successful, we initialize the
header (lines 37-39), make the message type M_PROTO (line 41),
link the M_DATA message to it (line 42), and pass it on (line
43).
See unbufcall(D3) for the other half of this example.
1 struct hdr {
2 uint_t h_size;
3 int h_version;
4 };
5 struct mod {
6 long m_id;
7 char m_type;
...
8 };
9 #define TIMEOUT 1
10 #define BUFCALL 2
...
11 modsrv(q) /* assume only M_DATA messages enqueued here */
12 queue_t *q;
13 {
14 mblk_t *bp;
15 mblk_t *mp;
16 struct hdr *hp;
17 struct mod *modp;
18 modp = (struct mod *)q->q_ptr;
19 while ((mp = getq(q)) != NULL) {
20 if (!canput(q->q_next)) {
21 putbq(q, mp);
22 return;
23 }
24 bp = allocb(sizeof(struct hdr), BPRI_MED);
25 if (bp == NULL) {
26 putbq(q, mp);
27 modp->m_id = bufcall(sizeof(struct hdr), BPRI_MED, modcall,
28 (long)q);
29 if (modp->m_id == 0) {
30 modp->m_id = timeout(modcall, (long)q, drv_usectohz(500000));
31 modp->m_type = TIMEOUT;
32 } else {
33 modp->m_type = BUFCALL;
34 }
35 return;
Copyright 1994 Novell, Inc. Page 3
bufcall(D3) bufcall(D3)
36 }
37 hp = (struct hdr *)bp->b_wptr;
38 hp->h_size = msgdsize(mp);
39 hp->h_version = 1;
40 bp->b_wptr += sizeof(struct hdr);
41 bp->b_datap->db_type = M_PROTO;
42 bp->b_cont = mp;
43 putnext(q, bp);
44 }
45 }
46 modcall(q)
47 queue_t *q;
48 {
49 struct mod *modp;
50 modp = (struct mod *)q->q_ptr;
51 modp->m_type = 0;
52 qenable(q);
53 }
Multithreaded Example
The purpose of this service routine [see srv(D2)] is to add a
header to all M_DATA messages. We assume only M_DATA messages
are added to its queue. Service routines must process all
messages on their queues before returning, or arrange to be
rescheduled.
While there are messages to be processed (line 21), we check
to see if we can send the message on in the stream. If not,
we put the message back on the queue (line 23) and return.
The STREAMS flow control mechanism will re-enable us later
when messages can be sent. If canputnext(D3) succeeded, we
try to allocate a buffer large enough to hold the header (line
26). If no buffer is available, the service routine must be
rescheduled later, when a buffer is available. We put the
original message back on the queue (line 28), lock the private
data structure, and use bufcall to attempt the rescheduling
(lines 30 and 31). If bufcall succeeds, we set the m_type
field in the module's private data structure to BUFCALL. If
bufcall failed, we use itimeout(D3) to reschedule us instead
(line 33). modcall will be called in about a half second
[drv_usectohz(500000)]. When the rescheduling has been done,
we unlock the private data structure and return.
When modcall runs, it will lock the private data structure and
set the m_type field to zero, indicating that there is no
outstanding request. Then the data structure is unlocked and
Copyright 1994 Novell, Inc. Page 4
bufcall(D3) bufcall(D3)
the queue's service routine is scheduled to run by calling
qenable(D3) (line 59).
If the buffer allocation is successful, we initialize the
header (lines 41-43), make the message type M_PROTO (line 45),
link the M_DATA message to it (line 46), and pass it on (line
47).
See unbufcall(D3) for the other half of this example.
1 struct hdr {
2 uint_t h_size;
3 int h_version;
4 };
5 struct mod {
6 toid_t m_id;
7 char m_type;
8 lock_t *m_lock;
. . .
9 };
10 #define TIMEOUT 1
11 #define BUFCALL 2
. . .
12 modsrv(q) /* assume only M_DATA messages enqueued here */
13 queue_t *q;
14 {
15 mblk_t *bp;
16 mblk_t *mp;
17 struct hdr *hp;
18 struct mod *modp;
19 pl_t pl;
20 modp = (struct mod *)q->q_ptr;
21 while ((mp = getq(q)) != NULL) {
22 if (!canputnext(q)) {
23 putbq(q, mp);
24 return;
25 }
26 bp = allocb(sizeof(struct hdr), BPRI_MED);
27 if (bp == NULL) {
28 putbq(q, mp);
29 pl = LOCK(modp->m_lock, plstr);
30 modp->m_id = bufcall(sizeof(struct hdr), BPRI_MED, modcall,
31 (long)q);
32 if (modp->m_id == 0) {
33 modp->m_id = itimeout(modcall, q, drv_usectohz(500000), plstr);
34 modp->m_type = TIMEOUT;
35 } else {
Copyright 1994 Novell, Inc. Page 5
bufcall(D3) bufcall(D3)
36 modp->m_type = BUFCALL;
37 }
38 UNLOCK(modp->m_lock, pl);
39 return;
40 }
41 hp = (struct hdr *)bp->b_wptr;
42 hp->h_size = msgdsize(mp);
43 hp->h_version = 1;
44 bp->b_wptr += sizeof(struct hdr);
45 bp->b_datap->db_type = M_PROTO;
46 bp->b_cont = mp;
47 putnext(q, bp);
48 }
49 }
50 modcall(q)
51 queue_t *q;
52 {
53 struct mod *modp;
54 pl_t pl;
55 modp = (struct mod *)q->q_ptr;
56 pl = LOCK(modp->m_lock, plstr);
57 modp->m_type = 0;
58 UNLOCK(modp->m_lock, pl);
59 qenable(q);
60 }
REFERENCES
allocb(D3), esballoc(D3), esbbcall(D3), itimeout(D3),
timeout(D3), unbufcall(D3)
NOTICES
Portability
All processors
Applicability
ddi: 1, 2, 3, 4, 5, 5mp, 6, 6mp, 7, 7mp
The bufcall function is currently defined to return a toid_t
data type. In earlier releases, it returned an int.
Copyright 1994 Novell, Inc. Page 6