mirror of
https://github.com/PDP-10/its.git
synced 2026-01-24 11:22:36 +00:00
579 lines
20 KiB
Plaintext
Executable File
579 lines
20 KiB
Plaintext
Executable File
;;; Copyright (c) 1999 Massachusetts Institute of Technology
|
||
;;; See the COPYING file at the top-level directory of this project.
|
||
|
||
COMMENT |
|
||
|
||
Notes on TCP input and output buffers
|
||
|
||
Ideally the TCP segment queues should be logically separate from the IP
|
||
datagram queues, but for efficiency it should be possible to have them
|
||
both together.
|
||
|
||
Input buffers are expected to be lists of datagrams/segments.
|
||
This is reasonably simple, since pointers (in known places) can just be
|
||
set up to the data (whereever it is in the datagram/segment).
|
||
There is a problem with allocation/windows in this scheme, since it
|
||
is possible to run out of datagram buffers before running out of window,
|
||
if the remote site becomes cretinous about it and sends only a few
|
||
bytes per dgram very fast. But in that case re-transmission can just
|
||
force it to buffer up its output, so that future dgrams will be somewhat
|
||
more filled, so it is probably OK. If this does become a screw, can
|
||
add code to do compaction at clock level.
|
||
|
||
Output buffers are a little more uncertain. Could have simple
|
||
circular buffer, with appropriate pointers set up so that IMP output
|
||
message is read directly from the buffer (same as NCP). Alternatively
|
||
could have queue of segments/datagrams all ready to go. Guess I'd
|
||
like to try doing the latter, to keep things "simple" by minimizing
|
||
the number of kinds of things floating around.
|
||
|
||
Problem with putting output datagram together: can't always predict
|
||
ahead of time how big the leaders are going to be! Especially true
|
||
for IP level, which TCP is not supposed to know too much about. Thus
|
||
if not careful, it could happen that output is put into a segment too
|
||
close to the start of the buffer, so that there is not enough room for
|
||
the TCP and IP headers to fit in. Have to look at this; may need to
|
||
give up notion of keeping all the packet internals nicely contiguous.
|
||
Maximum IP header length is set at 60 bytes (15 words). The maximum
|
||
TCP header length is also 15 words.
|
||
Looking at the currently defined options, it seems unlikely that ITS
|
||
will use any of them, and if so, they can be predicted fairly easily on
|
||
a per-connection basis, so things should work out okay. Note that
|
||
the IP level can always simply fragment stuff if it really wants to be
|
||
that complex.
|
||
|
||
It would be nice to be able to keep track of stuff which is on the
|
||
device (IMP) output queue but hasn't yet gone out, in order to add
|
||
last-minute bits (like ACK) or even some more data. Idea: output "queue"
|
||
is just a list of TCP connections that need attention, so can always
|
||
go in and mung stuff (even change mind about outputting) just by playing
|
||
with connection flags/lists. This is basically how NCP does it.
|
||
|
||
Re packet buffer design:
|
||
Somewhat more hypothetical is the notion of keeping a "usage
|
||
count" for each buffer, so that pointers from the packet table entries
|
||
can point to several different buffers and not just one. When a usage
|
||
count hits zero, put the buffer on the freelist. Actually this is not
|
||
really needed for the case of a table entry pointing to more than one
|
||
buffer, but it IS needed for the case of more than one table entry
|
||
pointing to the same buffer. This might happen, for example, if
|
||
an internet bypass were set up so that datagrams going to ourselves
|
||
were simply vectored directly to the input queues. But for the
|
||
time being, it probably isn't too outrageous to simply re-copy the
|
||
datagram in question. (Also better emulates a fake network device).
|
||
|
||
Suggest that lists point directly to themselves rather than
|
||
to start of entry; this allows low-level list routines to be used
|
||
on all lists. The higher-level routines of course have to know what
|
||
offsets to use for a specific list. Alternatively provide different routines
|
||
for each offset needed, and equate references to the appropriate routine
|
||
for each use. (This is what I'm trying at the moment, hence the IRPS)
|
||
|
||
May want to use format <head>,,<list ptr> where <head> is the addr
|
||
of the initial pointer (not initial node). This allows backtracking
|
||
to figure out what TCP connection or IP queue a packet belongs to.
|
||
Brute force approach is to simply have another word for the TCB index, etc.
|
||
|
||
If this stuff is general enough it could be used for CHAOS packets also.
|
||
|
|
||
|
||
COMMENT |
|
||
|
||
-- Lots of this is wrong now... --
|
||
|
||
Buffers are all 256 (400 octal) words long, and 4 of them fit on one
|
||
ITS page. This size was chosen because the maximum length of an IMP
|
||
message (not counting IMP-Host padding) is 255 words of 4 8-bit bytes
|
||
per word. This allows one extra word; not much. Taking away the
|
||
3 IMP leader words (which has to be done anyway for NCP to continue
|
||
working) gives us 4 spare words per buffer.
|
||
|
||
This still may not be enough. Rather than shoe-horn some clever stuff
|
||
into each buffer header, I am opting to maintain "Packet Entries" in a
|
||
"Packet Table" separate from the "Packet Buffers" themselves. All
|
||
messages/datagrams/segments/packets are identified by a pointer
|
||
into the table. (Could use index, as for sockets/TCBs, but this is
|
||
awkward for lists). The entry identified by the pointer will contain
|
||
the actual pointers into the buffer associated with that packet.
|
||
[NOTE: may want to have these pointers point into various places, not
|
||
necessarily all the same buffer. Must think about this.]
|
||
|
||
Free buffers are linked by a freelist pointer in their first word,
|
||
with an identifier in the second word to help GC. The only design
|
||
issue for the buffers themselves is how to set things up so that it is
|
||
easy to GC a large freelist, i.e. identify pages that have nothing
|
||
but free buffers in them and thus can be removed from the system's
|
||
address space. Currently I have simply adopted the strategem used for
|
||
CHAOS buffers (at CHCLN) to have the core job snuffle over the freelist.
|
||
|
||
Note that the low-level routines for manipulating lists are actually
|
||
referenced via macros which are given an offset as argument. This
|
||
offset refers to the offset between the node pointer and the location
|
||
of the "next" pointer in the node; the macro will assemble into a
|
||
call to the right routine for that offset. Currently only offsets
|
||
up to 2 are supported; any lists that the packet is put on must
|
||
be threaded through the first 3 words of the node, and the macros check
|
||
for this.
|
||
|
|
||
|
||
SUBTTL Packet Tracing Code
|
||
|
||
IFNDEF PKTTRC,PKTTRC==:0 ; Nonzero turns tracing code on
|
||
IF1,IFN PKTTRC,.ERR IP packet tracing code included.
|
||
|
||
COMMENT |
|
||
|
||
This code can be used to keep a history of what happens to a packet.
|
||
The history is stored in the packet buffer as a series of indexes
|
||
into a table of named events.
|
||
|
||
To trace a particular event use the TRCPKT macro.
|
||
|
||
TRCPKT(REG,"String")
|
||
|
||
REG is the register which currently contains an index to the packet
|
||
buffer table. The code is more efficient if the register is R. String
|
||
is the description of the event.
|
||
|
||
To generate the event table, you must call the TRCCOD macro somewhere
|
||
after the last call to TRCPKT.
|
||
|
||
|
|
||
|
||
%NTRCE==:77 ; Allow this many trace events. Value must be a bit mask (all
|
||
; ones) to work right. If you make it bigger than 77
|
||
; you have to change the "TRC%" in the macros to something
|
||
; shorter, too.
|
||
|
||
%%%TRC==1
|
||
|
||
; TRCCOD generates TRCTBL, which holds <code address,,address of event string>
|
||
; for each trace event.
|
||
DEFINE TRCCOD
|
||
IFN PKTTRC,[
|
||
TRC%0==:<0,,[ASCIZ /Null event/]>
|
||
TRCTBL: REPEAT %%%TRC,CONC TRC%,\.RPCNT
|
||
IF2, REPEAT %%%TRC,CONC EXPUNGE TRC%,\.RPCNT
|
||
]
|
||
TERMIN
|
||
|
||
DEFINE TRCPKT REG,&(EVENT)
|
||
IFN PKTTRC,[
|
||
IFG %%%TRC-%NTRCE,.ERR Too many packet trace events!
|
||
CONC TRC%,\%%%TRC,==:<.,,[ASCIZ EVENT]>
|
||
IFN REG-R,[
|
||
PUSH P,R
|
||
MOVE R,REG
|
||
]
|
||
PUSH P,TT
|
||
MOVEI TT,%%%TRC
|
||
PUSHJ P,PKTPTS
|
||
POP P,TT
|
||
IFN REG-R,[
|
||
POP P,R
|
||
]
|
||
%%%TRC==%%%TRC+1
|
||
]
|
||
TERMIN
|
||
|
||
IFN PKTTRC,[
|
||
|
||
; Store information in the packet history data buffer
|
||
; "Information" is a 6-bit quantity which indexes into a table
|
||
; of strings.
|
||
; TT/ Reason index
|
||
; R/ Pkt Buffer index
|
||
|
||
PKTPTS: CONO PI,PIOFF ; Freeze machine
|
||
DPB TT,PK.HSP(R) ; Store trace info
|
||
IBP PK.HSP(R) ; Bump BP
|
||
HRRZ TT,PK.HSP(R) ; Pick up history byte ref (address only)
|
||
CAIL TT,PK.HSP(R) ; If we've gotten to the bottom,
|
||
JRST [ MOVEI TT,PK.HST(R) ;Wrap around
|
||
HRLI TT,(<.BP %NTRCE_30.>) ; BP to left 6 bits
|
||
MOVEM TT,PK.HSP(R) ;Reset it to top
|
||
JRST .+1 ]
|
||
CONO PI,PION
|
||
POPJ P,
|
||
]
|
||
|
||
;;; Packet Table Entries
|
||
|
||
EBLK
|
||
IFNDEF PKBSIZ,PKBSIZ==1000 ; 512. words per packet buffer (2048 byte MTU)
|
||
IFNDEF NPKPGS,NPKPGS==40. ; # pages OK to use for packet buffers
|
||
NPKB==:<NPKPGS*<2000/PKBSIZ>> ; # packet buffers available
|
||
NPKE==:NPKB ; # packet entry nodes.
|
||
IFN NPKB-NPKE,.ERR You must fix the UFLS at PKTGF.
|
||
|
||
PKETBL: OFFSET -.
|
||
; General (device driver, etc)
|
||
PK.FLG:: 1,,PKETBL+PK.L ; General - <flags>,,<PE freelist or dev driver list>
|
||
%PKPIL==:<SETZ> ; Packet locked at PI level, being output
|
||
%PKODN==:<1000,,> ; Packet has been output (else not yet)
|
||
;;; %PKNOF==:<2000,,> ; Packet should not be freed when output done.
|
||
%PKFLS==:<4000,,> ; Flush pkt if seen on output queue (ie dont output)
|
||
%PKRTR==:<10000,,> ; Packet is being re-transmitted by TCP
|
||
.SEE %PQFLX ; Low bits of LH used for on-list flags.
|
||
|
||
PK.IP:: 0 ; IP Datagram - <IP Header ptr>,,<IP Datagram list>
|
||
; May be strung on Internet Queue, IP output queue
|
||
PK.TCP:: 0 ; TCP Segment - <TCP Header pointer>,,<TCP Segment list>
|
||
; May be strung on TCB input Q, output retransmit Q
|
||
PK.TCI:: 0 ; TCP Segment - <# bytes data><# bytes offset><TCB index>
|
||
PK%TDL==:<777700,,0> ; # octets of data in TCP segment
|
||
PK%TDO==:< 77,,770000> ; # octets data is offset from TCP header
|
||
PK%TCB==:< ,,007777> ; TCB connection index
|
||
PK$TDL==:<.BP PK%TDL,PK.TCI>
|
||
PK$TDO==:<.BP PK%TDO,PK.TCI>
|
||
PK$TCB==:<.BP PK%TCB,PK.TCI>
|
||
PK.BUF:: 0 ; General - <# wds>,,<addr of buffer>
|
||
PK.TIM:: 0 ; General - Time sent or received, int level (Sys time)
|
||
PK.DST:: 0 ; Immediate destination address if on output queue
|
||
PK.HWI:: 0 ; Hardware Information - currently KS Unibus address
|
||
|
||
IFN PKTTRC,[
|
||
PK.HST:: BLOCK 3 ; Packet trace history buffer
|
||
PK.HSP:: 0 ; Packet trace history pointer
|
||
]
|
||
|
||
PK.L:: OFFSET 0 ; Length of a Packet-Entry (PE) node
|
||
REPEAT <NPKE-1>,[
|
||
IFN .RPCNT-<NPKE-2>, 1,,.+PK.L ; Build initial freelist
|
||
.ELSE 1,,0
|
||
BLOCK PK.L-1
|
||
]
|
||
PKETBE==:.-PK.L ; Last legal PE pointer value
|
||
|
||
PKEQHF: PKETBE,,PKETBL ; Header for Packet-Entry node freelist
|
||
|
||
BBLK
|
||
|
||
; A "queue" is a list of nodes pointed to by a "queue header" word
|
||
; of format <last node>,,<first node>. Each node pointer points to
|
||
; the next node pointer (or zero if no more).
|
||
; There is a set of flags in the LH of a certain word, at offset
|
||
; PQ.FLG, that indicate which lists a node is currently on.
|
||
|
||
PQ.FLG==:PK.FLG ; Offset of word list-flags are in.
|
||
%PQFLX==0 ; Initial val
|
||
IFNDEF %%%QOF,%%%QOF==0 ; This gets set to highest offset supported
|
||
|
||
IRPS PKQGF,,[PKQGF0:PKQGF1:PKQGF2:]PKQPL,,[PKQPL0:PKQPL1:PKQPL2:]PKQPF,,[PKQPF0:PKQPF1:PKQPF2:]%PQFL,,[%PQFL0:%PQFL1:%PQFL2:]
|
||
IFG .IRPCNT-%%%QOF,%%%QOF==.IRPCNT
|
||
|
||
%PQFL==:<1_.IRPCNT,,> ; Def a flag in LH at offset PQ.FLG
|
||
%PQFLX==%PQFLX\%PQFL ; Mask of all list-flags def'd.
|
||
|
||
; PKQGF - Get first node from queue
|
||
; Q/ addr of queue header
|
||
; A/ addr of node (zero if none)
|
||
|
||
PKQGF: CONO PI,PIOFF ; Work at all levels
|
||
HRRZ A,(Q) ; Get 1st from queue header word
|
||
JUMPE A,PIONJ ; None, so return zero.
|
||
MOVSI T,(%PQFL) ; Now clear appropriate flag for list
|
||
XORB T,PQ.FLG(A) ; to indicate it's not on it any more.
|
||
TLNE T,(%PQFL) ; Paranoia plus
|
||
BUG HALT,[PK: GF node wasnt on list]
|
||
HRRZ T,.IRPCNT(A) ; Get 2nd
|
||
HRRM T,(Q) ; Make it 1st
|
||
CAIN T, ; If all's well, done.
|
||
SETZM (Q) ; Else must clear whole header
|
||
IFNDEF PIONJ,PIONJ:
|
||
CONO PI,PION
|
||
POPJ P,
|
||
|
||
; PKQPL - Put node on queue as last thing.
|
||
; Q/ addr of queue header
|
||
; A/ addr of node
|
||
PKQPL: TRNN A,-1 ; More paranoia
|
||
BUG HALT,[PK: zero node ptr]
|
||
HLLZS .IRPCNT(A) ; Say this node is last one
|
||
CONO PI,PIOFF ; Work at all levels
|
||
MOVSI T,(%PQFL) ; Paranoia: Set appropriate flag for list
|
||
XORB T,PQ.FLG(A) ; to indicate it's on it now.
|
||
TLNN T,(%PQFL) ; plus check...
|
||
BUG HALT,[PK: node already on list]
|
||
HLRZ T,(Q) ; Get last node
|
||
HRLM A,(Q) ; Point to new last node
|
||
JUMPN T,[HRRM A,.IRPCNT(T) ; Make prev last node point to new last
|
||
JRST .+2] ; Skip over next instr!!
|
||
HRRM A,(Q) ; Queue was empty, make this the new first too
|
||
CONO PI,PION
|
||
POPJ P,
|
||
|
||
; PKQPF - Put node on queue as first thing.
|
||
; Q/ addr of queue header
|
||
; A/ addr of node
|
||
PKQPF: TRNN A,-1 ; Yes more paranoia
|
||
BUG HALT,[PK: zero node ptr]
|
||
CONO PI,PIOFF
|
||
MOVSI T,(%PQFL) ; Paranoia: Set appropriate flag for list
|
||
XORB T,PQ.FLG(A) ; to indicate it's on it now.
|
||
TLNN T,(%PQFL) ; check...
|
||
BUG HALT,[PK: node already on list]
|
||
HRRZ T,(Q) ; Get first thing
|
||
CAIN A,(T) ; paranoia, avoid loops to self
|
||
BUG
|
||
HRRM T,.IRPCNT(A) ; Make it second thing
|
||
HRRM A,(Q) ; Make new first thing
|
||
CAIN T,
|
||
HRLM A,(Q) ; Was empty, also make it last thing.
|
||
CONO PI,PION
|
||
POPJ P,
|
||
TERMIN
|
||
|
||
; Define PKQGF, etc so that they actually reference PKQGF0, etc as
|
||
; appropriate for the given offset.
|
||
IRP RTN,,[PKQGF,PKQPF,PKQPL]
|
||
DEFINE RTN ?OFFST=0,
|
||
CONC RTN,\OFFST
|
||
IFG OFFST-%%%QOF,.ERR RTN used with bad offset
|
||
TERMIN
|
||
TERMIN
|
||
|
||
|
||
; PKEGF - Get a free Packet-Entry node
|
||
; Clears node contents.
|
||
; Clobbers Q,T
|
||
; Returns A/ PE ptr (0 if none)
|
||
|
||
PKEGF: MOVEI Q,PKEQHF
|
||
IFE PKTTRC,[
|
||
CALRET PKQGF ; Get a node
|
||
]
|
||
IFN PKTTRC,[
|
||
CALL PKQGF
|
||
JUMPE A,CPOPJ ; No packet
|
||
SETZM PK.HST(A)
|
||
SETZM PK.HST+1(A)
|
||
SETZM PK.HST+2(A)
|
||
MOVEI Q,PK.HST(A) ; Build byte ref to history trail
|
||
HRLI Q,(<.BP %NTRCE_30.>) ; (dpb ref)
|
||
MOVEM Q,PK.HSP(A) ; save ref
|
||
POPJ P,
|
||
]
|
||
|
||
; PKERT - Return a Packet-Entry node to freelist
|
||
; A/ PE ptr to node
|
||
; Clobbers Q,T
|
||
|
||
PKERT: MOVEI Q,PKEQHF ; Use Packet-Entry freelist
|
||
CALRET PKQPF ; Put back on start of list.
|
||
|
||
; Note that all MP calls to the routines below which allocate or free
|
||
; entries/buffers must be sure not to block (page fault or UFLS)
|
||
; while any "loose" entries/buffers exist (not pointed to by any list)
|
||
; unless there something on the PCLSR locked-switch list which will return the
|
||
; currently "loose" entry/buffer to its freelist -- otherwise
|
||
; it is possible for "loose" stuff to slowly accumulate.
|
||
|
||
; PKTGF - Get a free Packet-Entry node and Packet Buffer. Hangs until
|
||
; it wins. Note that it depends on fact there is one PE node
|
||
; for every packet buffer, and vice versa! If this becomes untrue
|
||
; then the way it UFLSes should be fixed up.
|
||
; PKTGFI - version that skips if wins, doesn't hang.
|
||
; Returns A/ PE ptr Clobbers Q,T
|
||
|
||
PKTGF: SKIPN PKEQHF ; Fast check, see if any packet entries/buffers free
|
||
CALL UFLS ; Nope, hang until something turns up.
|
||
CALL PKTGFI ; Get a entry/buffer!
|
||
JRST PKTGF ; None? Sigh, go hang.
|
||
RET
|
||
|
||
PKTGFI: CALL PKEGF ; Get a free node
|
||
JUMPE A,CPOPJ
|
||
PUSH P,A ; Save pointer to it
|
||
PKTGF1: CALL PKBGF ; Get a free buffer
|
||
JUMPN A,PKTGF8 ; Jump if found one right away!
|
||
CALL PKBAL ; None left on freelist, try to allocate more.
|
||
CAIA ; Sigh, failed.
|
||
JRST PKTGF1 ; Won, go pluck a buffer from freelist.
|
||
|
||
; Lost, can't get any more buffers.
|
||
POP P,A ; None available, take non-skip return
|
||
CALRET PKERT ; Put PE node back on its freelist.
|
||
|
||
; Won, store buffer pointer in PE.
|
||
PKTGF8: MOVE T,A
|
||
POP P,A ; Restore PE ptr
|
||
MOVEM T,PK.BUF(A)
|
||
MOVE T,PQ.FLG(A) ; Paranoia dept, verify not on any lists.
|
||
TLNE T,(%PQFLX)
|
||
BUG HALT,[PK: Freelist node not free!]
|
||
SETZM PK.FLG(A) ; Zap all other entries in packet node.
|
||
SETZM PK.IP(A)
|
||
SETZM PK.TCP(A)
|
||
SETZM PK.TCI(A)
|
||
SETZM PK.TIM(A)
|
||
AOS (P) ; Win, skip on return!
|
||
RET
|
||
|
||
; PKTRT - Return both a Packet-Entry and its associated buffer to freelist
|
||
; only if check shows that it doesn't belong to any lists.
|
||
; PKTRTA - Always return to freelist. If check shows that it is still
|
||
; on some list, bad error!
|
||
; Clobbers A,Q,T
|
||
; A/ PE ptr (must be off all lists)
|
||
|
||
PKTRTA: CAIL A,PKETBL ; Paranoia check for legal pointers
|
||
CAILE A,PKETBE
|
||
BUG HALT,[PK: Bad PE pointer]
|
||
MOVE T,PQ.FLG(A)
|
||
TLNE T,(%PQFLX\%PKPIL) ; On list or locked at I/O PI level?
|
||
BUG HALT,[PK: Freeing packet still in use!]
|
||
JRST PKTRTX ; Nope, can proceed to put on freelist.
|
||
|
||
PKTRT: CAIL A,PKETBL ; Paranoia check for legal pointers
|
||
CAILE A,PKETBE
|
||
BUG HALT,[PK: Bad PE pointer]
|
||
MOVE T,PQ.FLG(A)
|
||
TLNE T,(%PQFLX\%PKPIL) ; On a list or locked by interface PI level?
|
||
RET ; Yes, don't return to freelist yet.
|
||
PKTRTX: PUSH P,A ; Save PE ptr
|
||
SKIPE A,PK.BUF(A) ; Get buffer pointer associated with PE
|
||
CALL PKBRT ; Return the buffer
|
||
POP P,A
|
||
SETZM PK.BUF(A) ; Ensure buffer pointer zapped.
|
||
CALRET PKERT ; Then return the packet entry
|
||
|
||
; PKTPCL - Return a packet entry/buffer while PCLSR'ing.
|
||
; This is the standard LOSSET routine to use.
|
||
; A must hold the PE ptr at time of the block (which we are backing
|
||
; out of).
|
||
; Must only clobber A and T!!
|
||
|
||
PKTPCL: MOVE A,AC0S+A(U) ; Get ac A at time of the block
|
||
PUSH P,Q ; Mustn't clobber Q
|
||
CALL PKTRT ; Return the entry/buffer (clobbers Q,T)
|
||
JRST POPQJ
|
||
|
||
|
||
EBLK
|
||
PKBNF: 0 ; # free Packet Buffers
|
||
PKBNT: 0 ; # total Packet Buffers
|
||
PKBCTM: 0 ; Time of last no-more-core complaint
|
||
PKBQHF: 0 ; Queue Header for buffer freelist
|
||
PKBQHC: 0 ; Queue Header for core job cleanup
|
||
BBLK
|
||
|
||
; PKBGF - Get a free Packet Buffer
|
||
; Clobbers Q,T
|
||
; Returns A/ PB ptr (0 if none)
|
||
|
||
PKBGF: MOVEI Q,PKBQHF ; Point to buffer freelist
|
||
CALL PKQGF ; Get first thing off it
|
||
JUMPE A,CPOPJ ; If got nothing, just return.
|
||
SETZM 1(A) ; Aha, got it! Flush free-buffer identifier.
|
||
SOS PKBNF ; Decrement # free packet buffers.
|
||
RET
|
||
|
||
; PKBRT - Return a Packet Buffer to freelist. Puts back at END of freelist,
|
||
; as PKBCLN clean-up depends on this.
|
||
; Clobbers Q,T
|
||
PKBRT: SETZM (A) ; Paranoia aid - clear "flags" in LH of 1st wd.
|
||
; Otherwise PKQ routines complain.
|
||
MOVE T,[SIXBIT /BRUNCH/]
|
||
MOVEM T,1(A) ; Set up free-buffer identifier
|
||
AOS PKBNF ; Increment # free packet buffers.
|
||
MOVEI Q,PKBQHF ; Point to buffer freelist
|
||
CALRET PKQPL ; Put it back on, at end.
|
||
|
||
; PKBRTL - Return a list of Packet Buffers to freelist
|
||
; Q/ ptr to queue header of list
|
||
; Clobbers A,T
|
||
PKBRTL: CALL PKQGF ; Get first thing off list
|
||
JUMPE A,CPOPJ
|
||
PUSH P,Q
|
||
CALL PKBRT ; Return it to buffer freelist
|
||
POP P,Q
|
||
JRST PKBRTL
|
||
|
||
; PKBAL - Allocate more Packet Buffers
|
||
; Clobbers A,Q,T
|
||
; Returns .+1 if lost
|
||
; .+2 if won (must still call PKBGF to get a buffer from list)
|
||
|
||
PKBAL: PUSH P,B
|
||
CONI PI,Q ; Save PI channel-on status
|
||
ANDI Q,177
|
||
CONO PI,UTCOFF ; Make the world safe for IOMQ
|
||
MOVE B,PKBNT ; Check total # of buffers so far
|
||
CAIL B,NPKB ; Make sure we're not already using max allowed
|
||
JRST PKBAL4 ; Ugh, already at max! Go complain.
|
||
PUSHJ P,IOMQ ; Get 1K of memory
|
||
JRST PKBAL3 ; Mem not available, fail
|
||
CONO PI,PICON(Q) ; Won, restore PI status
|
||
MOVEI B,MUPKT ; Set page type = packet
|
||
DPB B,[MUR,,MEMBLT(A)]
|
||
LSH A,10. ; Turn allocated page # into mem address
|
||
HRLI A,-<2000/PKBSIZ> ; Make AOBJN into page (# buffers per page)
|
||
PKBAL2: PUSHJ P,PKBRT ; Put them all on free list
|
||
ADDI A,PKBSIZ-1
|
||
AOBJN A,PKBAL2
|
||
MOVEI B,<2000/PKBSIZ> ; This many more buffers have been created
|
||
ADDM B,PKBNT ; Increase total (PKBNF bumped by PKBRT)
|
||
POP P,B
|
||
AOS (P) ; Take win return.
|
||
RET
|
||
|
||
; Here if packet stuff trying to use up too much core
|
||
PKBAL4: MOVE B,PKBCTM ; Don't complain too often
|
||
ADDI B,60.*30. ; Just once a minute
|
||
CAMLE B,TIME
|
||
JRST PKBAL3
|
||
BUG CHECK,[PACKET NET ATTEMPTING TO USE TOO MUCH CORE]
|
||
MOVE B,TIME
|
||
MOVEM B,PKBCTM
|
||
PKBAL3: CONO PI,PICON(Q) ; Lost, restore PI status
|
||
POP P,B
|
||
POPJ P, ; and take error return.
|
||
|
||
|
||
; PKBCLN - Called only by core job, to clean up packet buffers.
|
||
; Smashes all ACs.
|
||
|
||
PKBCLN: SKIPE A,PKBNT ; See if 2/3 or more of buffers free
|
||
SKIPN B,PKBNF
|
||
POPJ P, ; No buffers or none free, nothing to do
|
||
SUBM A,B
|
||
IDIV A,B ; Get ratio of total to used
|
||
CAIGE A,3 ; Note if B is zero, A is unchanged
|
||
POPJ P, ; and at least 32.
|
||
IFL TSYSM-256., MOVEI D,TSYSM-1 ; Scan memory for packet buffer pages
|
||
.ELSE MOVEI D,255.
|
||
PKBCL0: LDB A,[MUR,,MEMBLT(D)]
|
||
CAIE A,MUPKT
|
||
PKBCL4: SOJGE D,PKBCL0
|
||
JUMPL D,CPOPJ
|
||
MOVE A,D ; Quickly determine if any non-free buffers
|
||
LSH A,10. ; on this page
|
||
HRLI A,-<2000/PKBSIZ>
|
||
MOVE T,[SIXBIT/BRUNCH/]
|
||
PKBCL5: CAME T,1(A)
|
||
JRST PKBCL4 ; Not free, don't bother with slow stuff
|
||
ADDI A,PKBSIZ-1
|
||
AOBJN A,PKBCL5
|
||
SETZB C,PKBQHC ; Collect all free buffers on this page
|
||
MOVE E,PKBNF ; Loop about as many times as there are free buffers
|
||
PKBCL1: PUSHJ P,PKBGF ; Get next free buffer
|
||
JUMPE A,PKBCL2
|
||
LDB B,[121000,,A]
|
||
CAMN B,D
|
||
JRST [ MOVEI Q,PKBQHC ; This one's on the page, save it
|
||
PUSHJ P,PKQPL
|
||
AOJA C,.+2 ] ; Count them
|
||
PUSHJ P,PKBRT ; Not on the page, put back. This depends on
|
||
; the fact PKBRT puts back at END of list!
|
||
SOJG E,PKBCL1
|
||
PKBCL2: CAIE C,<2000/PKBSIZ> ; Did we get the whole page?
|
||
JRST [ MOVEI Q,PKBQHC ; No, must punt this one, and
|
||
PUSHJ P,PKBRTL ; return all the buffers we saved up.
|
||
JRST PKBCL4]
|
||
MOVNS C ; Yes, get rid of these buffers
|
||
ADDM C,PKBNT ; Decrement total # of buffers in use
|
||
MOVE A,D
|
||
PUSHJ P,MEMR ; Flush the page from addr space
|
||
JRST PKBCLN ; Back to flush more, until quota done.
|