DOC HOME SITE MAP MAN PAGES GNU INFO SEARCH PRINT BOOK
 
SVR5 and SCO OpenServer 5

bufcall(D3str)


bufcall -- call a function when a buffer becomes available

Synopsis (DDI)

   #include <sys/types.h>
   #include <sys/stream.h>
   #include <sys/ddi.h>
   

toid_t bufcall(uint_t size, int pri, void (*func)(), long arg);

Synopsis (ODDI)

   #include <sys/types.h>
   #include <sys/stream.h>
   

int bufcall(uint_t size, int pri, void (*func)(), long arg);

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.

Arguments


size
Number of bytes in the buffer to be allocated (from the failed allocb(D3str) request).

pri
Priority of the allocb allocation request that gives a hint to the allocator indicating how badly the buffer is needed. Valid values are:


BPRI_LO
Use for normal data allocations.

BPRI_MED
Use for other non-critical allocations.

BPRI_HI
Use for allocations that absolutely must succeed, although success is not guaranteed.

Some implementations may choose to ignore this argument.


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.

Return values (DDI)

On success, bufcall returns a non-zero value that identifies the scheduling request. On failure, bufcall returns 0.

Return values (DDI)

bufcall( ) returns 1 on success, indicating that the request has been successfuly recorded, or 0 on failure. On a failure return, func will never be called. A failure indicates either a (temporary) inability to allocate required internal data structures or that there are no available buffers of the requested size at the pri value.

Usage

When func runs, all interrupts from STREAMS devices are blocked. On multiprocessor systems, when func runs, all interrupts from STREAMS devices are blocked on the processor on which func is running. func has no user context and may not call any function that blocks.

Even when func is called, allocb can still fail if another module or driver had allocated the memory before func is able to call allocb.

The non-zero identifier returned by bufcall may be passed to unbufcall(D3str) to cancel the request.

Context

Base or Interrupt.

func executes at interrupt context.

Synchronization constraints

Does not block.

Driver-defined basic locks, read/write locks, and sleep locks may be held across calls to this function.

Singlethreaded example

The purpose of the srv(D2str) service routine is to add a header to all M_DATA(D7str) 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(D3str) 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 sets the m_type member to zero, indicating that there is no outstanding request. Then the queue's service routine is scheduled to run by calling qenable(D3str).

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(D3str) 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; 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 the srv(D2str) service routine is to add a header to all M_DATA(D7str) 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(D3str) 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 locks the private data structure and sets the m_type member to zero, indicating that there is no outstanding request. Then the data structure is unlocked and the queue's service routine is scheduled to run by calling qenable(D3str) (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(D3str) 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 { 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 }

Hardware applicability

All

Version applicability

ddi: 1, 2, 3, 4, 5, 5mp, 6, 6mp, 7, 7mp, 7.1, 7.1mp, 8, 8mp
oddi: 1, 2, 2mp, 3, 3mp, 4, 4mp, 5, 5mp, 6, 6mp

Differences between versions

The bufcall function is currently defined to return a toid_t data type. In earlier DDI versions and in ODDI, it returns an int.

References

allocb(D3str), esballoc(D3str), esbbcall(D3str), itimeout(D3), timeout(D3), unbufcall(D3str)
19 June 2005
© 2005 The SCO Group, Inc. All rights reserved.
OpenServer 6 and UnixWare (SVR5) HDK - June 2005