V9/sys/net.old/packet.c
/*
* P A C K E T
*
* Utilities for working with "packets". These are groups of (stream) data
* blocks terminated by a delimiter. When these blocks are removed from a
* queue, they are kept as a chain of blocks, without the final M_DELIM, and
* are referred to as a dequeued packet.
*
*
* Written by Kurt Gollhardt (Nirvonics, Inc.)
* Last update Wed Feb 12 21:24:10 1986
*
*/
#include "../h/param.h"
#include "../h/stream.h"
#define PKT_COMPLETE 0
#define PKT_PARTIAL 1
/*
* This routine does all the work for a server on a queue using packets.
* Arguments provide routines to do the application-specific operations:
* 'blocked' is called with a pointer to the queue;
* it should return non-zero if the location which the first
* packet on the queue would be sent to is full. If it does
* return non-zero, it must ensure that this queue will be
* re-enabled when the blocking condition goes away.
* 'allow_partial' is called with a pointer to the queue;
* it should return non-zero if this queue is allowed to receive
* partial packets at any time. (Any queue may be given partial
* packets if not doing so would cause a deadlock.)
* 'process' is called with a pointer to the queue, a dequeued
* packet (struct block *), and a flag which is non-zero if
* this is a partial packet.
*/
packet_srvp(q, delim_count, blocked, allow_partial, process, other)
register struct queue *q; /* queue being serviced */
int *delim_count; /* ptr to count of delims on queue */
int (*blocked)(); /* routine to see if a queue blocks */
int (*allow_partial)(); /* routine to say if partial pkts ok */
int (*process)(); /* routine to process a packet */
int (*other)(); /* routine to handle misc blocks */
{
register struct block *bp, *first_bp, *prev_bp;
register int processed = 0;
static check_other();
check_other(q, other);
while (*delim_count > 0) {
if ((*blocked) (q))
return;
if ((bp = getq(q))->type == M_DELIM)
first_bp = NULL;
else {
first_bp = prev_bp = bp;
while ((bp = getq(q))->type != M_DELIM)
prev_bp = bp;
prev_bp->next = NULL;
}
freeb(bp);
(*process) (q, first_bp, PKT_COMPLETE);
--*delim_count;
++processed;
check_other(q, other);
}
if (q->first != NULL) {
if ((*allow_partial) (q) || /* Huge partial packet */
((q->flag & QWANTW) && processed == 0)) {
if ((*blocked) (q))
return;
first_bp = q->first;
while (getq(q))
;
(*process) (q, first_bp, PKT_PARTIAL);
} else
q->flag |= QWANTR;
}
}
static check_other(q, other)
register struct queue *q;
int (*other)();
{
register struct block *bp;
while (q->first && q->first->type != M_DATA && q->first->type != M_DELIM) {
bp = getq(q);
if (other)
(*other)(q, bp);
else
freeb(bp);
}
}
/*
* This routine does all the work for a put routine on a queue using packets.
* Arguments provide routines to do the application-specific operations:
* 'ioctl' (optional) is called with a pointer to the queue, and a pointer
* to an M_IOCTL block.
* 'other' (optional) is called with a pointer to the queue, and a pointer
* to a block of type other than M_DATA, M_DELIM, or M_IOCTL.
*/
packet_putp(q, bp, delim_count, ioctl, other)
register struct queue *q; /* queue being put onto */
register struct block *bp; /* block being put on */
int *delim_count; /* ptr to count of delims on queue */
int (*ioctl)(); /* routine to handle an ioctl */
int (*other)(); /* routine to handle misc blocks */
{
register int ps, (*func)();
switch (bp->type) {
case M_DATA:
putq(q, bp);
return;
case M_DELIM:
ps = spl6();
putq(q, bp);
++*delim_count;
qenable(q);
splx(ps);
return;
case M_IOCTL:
func = ioctl;
break;
default:
func = other;
}
if (func)
(*func) (q, bp);
else if (q->next)
(*q->next->qinfo->putp) (q->next, bp);
else
freeb(bp);
}
#define BLEN(bp) ((bp)->wptr - (bp)->rptr)
#define BSZ(bp) ((bp)->lim - (bp)->base)
#undef MIN
#define MIN(a,b) ((a) > (b) ? (b) : (a))
/*
* This routine takes a dequeued packet and a length.
* It makes sure that the specified length of data appears in the first
* block of the packet, if possible.
*/
struct block *packet_pullup(bp, len)
register struct block *bp; /* the dequeued packet */
unsigned len; /* desired length */
/* returns the new packet (the old one is invalid) */
{
register struct block *nbp;
register unsigned count;
if (bp == NULL)
goto bad;
if (BLEN(bp) >= len)
return bp;
nbp = allocb(len);
if (nbp == NULL || BSZ(nbp) < len) {
free_blocks(bp);
goto cleanup;
}
nbp->next = bp;
while (bp && bp->type == M_DATA) {
count = BSZ(nbp) - BLEN(nbp); /* space left in new block */
count = MIN(count, len); /* clip to desired length */
count = MIN(count, BLEN(bp)); /* clip to length of this block */
bcopy(bp->rptr, nbp->wptr, count);
len -= count;
bp->rptr += count;
nbp->wptr += count;
if (BLEN(bp) > 0)
break;
nbp->next = bp->next;
freeb(bp);
bp = nbp->next;
}
if (len == 0)
return nbp;
cleanup:
free_blocks(nbp);
bad:
printf("packet_pullup bad\n");
return NULL;
}
/*
* Free space taken by a dequeued packet.
*/
free_blocks(bp)
register struct block *bp; /* dequeued packet */
{
register struct block *nbp;
while (bp) {
nbp = bp->next;
freeb(bp);
bp = nbp;
}
}
/*
* This routine puts blocks from a dequeued packet onto a queue's successor.
*/
packet_put(q, bp)
register struct queue *q; /* Note that we put to q->next */
register struct block *bp;
{
register struct block *bnext;
while (bp != NULL) {
bnext = bp->next;
if (bp->rptr >= bp->wptr)
freeb(bp);
else
(*q->next->qinfo->putp) (q->next, bp);
bp = bnext;
}
if (q->flag & QDELIM)
putctl(q->next, M_DELIM);
}