-- File: Pup10MBitImpl.mesa - last edit:
-- AOF 22-Feb-86 17:03:19
-- HGM 7-Dec-85 18:25:26
-- Copyright (C) 1985, 1986 by Xerox Corporation. All rights reserved.
DIRECTORY
Buffer USING [Buffer, GetBuffer, Type],
CommFlags USING [doDebug],
CommHeap USING [zone],
Driver USING [Network],
DriverTypes USING [Encapsulation],
Process USING [
DisableTimeout, EnableAborts, SecondsToTicks, SetPriority,
SetTimeout, Abort],
ProcessPriorities USING [priorityIOHigh],
Protocol1 USING [EncapsulatorProc, DecapsulatorProc],
PupPktOps USING [pupBuffers],
Pup10MBit USING [],
PupRouterDefs USING [ContextObject, NetworkContext],
PupStream USING [],
PupTypes USING [allHosts, PupHostID],
SpecialSystem USING [GetProcessorID, HostNumber, ProcessorID],
System USING [
broadcastHostNumber, GetClockPulses, HostNumber, MicrosecondsToPulses,
nullHostNumber, Pulses];
Pup10MBitImpl: MONITOR
IMPORTS
Process, Buffer, PupPktOps, CommHeap, SpecialSystem, System
EXPORTS Buffer, Pup10MBit, System =
BEGIN
Network: PUBLIC TYPE = Driver.Network;
HostNumber: PUBLIC TYPE = SpecialSystem.HostNumber;
chain: Handle ← NIL;
Handle: PUBLIC TYPE = LONG POINTER TO Object;
Object: PUBLIC TYPE = RECORD[
depth: CARDINAL ← 0,
me, all, head: TranslateEntry ← NIL,
event: CONDITION,
demon: PROCESS ← NIL,
driver: Network,
next: Handle];
TranslatePair: TYPE = LONG POINTER TO TranslateRecord;
TranslateRecord: TYPE = MACHINE DEPENDENT RECORD [
nsAddr: HostNumber, pupHost: PupTypes.PupHostID, filler: [0..377B]];
TranslateEntry: TYPE = LONG POINTER TO TranslateObject;
TranslateObject: TYPE = RECORD [
nextLink: TranslateEntry,
translatePair: TranslateRecord,
tries: CARDINAL,
timeStamp: System.Pulses,
status: TranslateStatus];
TranslateStatus: TYPE = {new, pending, active, zombie};
--constants
retryLimit: CARDINAL = 10B;
retryPulses: System.Pulses ← System.MicrosecondsToPulses[2000000];
deactivatePulses: System.Pulses ← System.MicrosecondsToPulses[180000000];
translationRequest: CARDINAL = 10101B;
translationResponse: CARDINAL = 7070B;
myNSHost: SpecialSystem.ProcessorID = SpecialSystem.GetProcessorID[];
Capsulators: PUBLIC PROC RETURNS[
decap: Protocol1.DecapsulatorProc, encap: Protocol1.EncapsulatorProc] =
{RETURN[DecapsulatePupEthernet, EncapsulatePupEthernet]};
Create: PUBLIC PROC[driver: Network, host: PupTypes.PupHostID]
RETURNS[xlate: Handle] =
BEGIN
xlate ← CommHeap.zone.NEW[Object];
xlate.me ← xlate.all ← xlate.head ← NIL;
xlate.demon ← NIL;
xlate.driver ← driver;
xlate.next ← NIL;
Process.EnableAborts[@xlate.event];
xlate.all ← AddTranslateRecord[xlate,
[System.broadcastHostNumber, PupTypes.allHosts, 0]];
xlate.me ← AddTranslateRecord[xlate, [myNSHost, [host], 0]];
xlate.demon ← FORK Demon[driver, xlate];
IF chain = NIL THEN chain ← xlate
ELSE
FOR finger: Handle ← chain, finger.next DO
IF finger.next # NIL THEN LOOP;
finger.next ← xlate;
EXIT;
ENDLOOP;
END; --Create
Destroy: PUBLIC PROC[xlate: Handle] =
BEGIN
IF xlate.demon # NIL THEN {Process.Abort[xlate.demon]; JOIN xlate.demon};
--cleanup in case demon was never running
UNTIL xlate.head = NIL DO
e: TranslateEntry ← xlate.head;
xlate.head ← e.nextLink;
CommHeap.zone.FREE[@e];
ENDLOOP;
IF chain = xlate THEN chain ← chain.next
ELSE
FOR finger: Handle ← chain, finger.next UNTIL finger.next = NIL DO
IF finger.next # xlate THEN LOOP;
finger.next ← finger.next.next;
EXIT;
ENDLOOP;
END; --Delete
EncapsulatePupEthernet: PUBLIC Protocol1.EncapsulatorProc =
--EncapsulatorProc: TYPE = PROC[b: Buffer.Buffer, immediate: LONG POINTER];
BEGIN
OPEN context: LOOPHOLE[b.context, PupRouterDefs.NetworkContext];
destination: CARDINAL ← immediate↑;
nsAddr: HostNumber ← Translate[context.protocol, [destination]];
b.encapsulation ←
[ethernet[ethernetDest: nsAddr,
ethernetSource: myNSHost,
ethernetType: pup]];
b.driver.length ← (b.pup.pupLength + 1)/2 + SIZE[DriverTypes.Encapsulation];
END; --EncapsulatePupEthernet
DecapsulatePupEthernet: PUBLIC Protocol1.DecapsulatorProc =
--DecapsulatorProc: TYPE = PROC[b: Buffer.Buffer] RETURNS[Buffer.Type];
BEGIN
bytes: CARDINAL;
ecap: CARDINAL = SIZE[DriverTypes.Encapsulation];
IF b.driver.length < ecap THEN RETURN[orphan]; --bad no matter what
SELECT b.encapsulation.ethernetType FROM
translation => {ReceiveTranslate[b]; RETURN[orphan]}; --one of those
# pup => RETURN[vagrant]; --still unknown
ENDCASE;
bytes ← (b.driver.length - ecap)*2;
IF bytes < b.pup.pupLength THEN RETURN[orphan] ELSE RETURN[pup];
END; --DecapsulatePupEthernet
Translate: PUBLIC ENTRY PROC[xlate: Handle, pupHost: PupTypes.PupHostID]
RETURNS [nsAddr: HostNumber ← System.nullHostNumber] =
BEGIN
<<
NOTE: Only the 'active' arm assigns 'nsAddr' a value that won't be discarded.
The default value assigned above will cause the driver to drop the packet.
>>
e: TranslateEntry;
SELECT TRUE FROM
((e ← FindTranslate[xlate, pupHost]) = NIL) =>
BEGIN
e ← CommHeap.zone.NEW[TranslateObject];
AddTranslate[xlate, e];
e.translatePair.pupHost ← pupHost;
NOTIFY xlate.event;
e.status ← new; e.tries ← 0;
e.timeStamp ← System.GetClockPulses[];
END;
(e.status = active) =>
BEGIN
IF e # xlate.head THEN
{RemoveTranslate[xlate, e]; AddTranslate[xlate, e]};
nsAddr ← e.translatePair.nsAddr;
e.timeStamp ← System.GetClockPulses[];
END;
(e.status = zombie) =>
BEGIN
IF e # xlate.head THEN
{RemoveTranslate[xlate, e]; AddTranslate[xlate, e]};
NOTIFY xlate.event;
e.status ← new; e.tries ← 0;
e.timeStamp ← System.GetClockPulses[];
END;
--(e.status = pending | new) => NULL;
ENDCASE;
END;
--interface
FindTranslate: INTERNAL PROC[xlate: Handle, pupHost: PupTypes.PupHostID]
RETURNS [entry: TranslateEntry] =
BEGIN
entry ← xlate.head;
WHILE entry # NIL DO
IF pupHost = entry.translatePair.pupHost THEN RETURN;
entry ← entry.nextLink;
ENDLOOP;
END;
AddTranslate: INTERNAL PROC[xlate: Handle, entry: TranslateEntry] =
BEGIN
entry.nextLink ← xlate.head;
xlate.head ← entry;
xlate.depth ← xlate.depth + 1;
END; --AddTranslate
RemoveTranslate: INTERNAL PROC[xlate: Handle, entry: TranslateEntry] =
BEGIN
e, pred: TranslateEntry;
IF (pred ← xlate.head) = entry THEN
{xlate.head ← xlate.head.nextLink; RETURN};
FOR e ← pred.nextLink, e.nextLink UNTIL e = NIL DO
IF e # entry THEN pred ← e
ELSE
BEGIN
pred.nextLink ← entry.nextLink;
xlate.depth ← xlate.depth - 1;
EXIT;
END;
ENDLOOP;
END;
AddTranslateRecord: ENTRY PROC[xlate: Handle, pair: TranslateRecord]
RETURNS [e: TranslateEntry] =
BEGIN
e ← FindTranslate[xlate, pair.pupHost];
SELECT e FROM
(NIL) => {e ← CommHeap.zone.NEW[TranslateObject]; AddTranslate[xlate, e]};
(xlate.all), (xlate.me) => RETURN;
ENDCASE;
e.translatePair ← pair; e.status ← active;
e.timeStamp ← System.GetClockPulses[];
END;
Demon: ENTRY PROC[driver: Network, xlate: Handle] =
BEGIN
SendRequest: INTERNAL PROC[] =
BEGIN
b: Buffer.Buffer;
request: TranslatePair;
b ← Buffer.GetBuffer[, PupPktOps.pupBuffers, send, smallBuffer, FALSE];
IF b # NIL THEN
BEGIN
b.encapsulation ←
[ethernet[
ethernetDest: System.broadcastHostNumber,
ethernetSource: myNSHost,
ethernetType: translation]];
b.driver.length ← SIZE[DriverTypes.Encapsulation] +
2*SIZE[TranslateRecord] + 1;
b.rawWords[0] ← translationRequest;
request ← LOOPHOLE[@b.rawWords[1]];
request↑ ← e.translatePair;
--also send our addresses, so responder does not fault
request ← request + SIZE[TranslateRecord];
request↑ ← xlate.me.translatePair;
driver.sendRawBuffer[b];
END;
END; --SendRequest
age: System.Pulses;
e, nextE: TranslateEntry;
pendingEntries: BOOLEAN;
Process.SetPriority[ProcessPriorities.priorityIOHigh];
--UNTIL ABORTED-- DO
ENABLE ABORTED => EXIT;
WAIT xlate.event;
pendingEntries ← FALSE;
FOR e ← xlate.head, nextE UNTIL e = NIL DO
nextE ← e.nextLink;
age ← [System.GetClockPulses[] - e.timeStamp];
SELECT e.status FROM
active, zombie =>
BEGIN
SELECT TRUE FROM
(age <= deactivatePulses) => NULL;
(e = xlate.me) => e.timeStamp ← System.GetClockPulses[];
(e = xlate.all) => e.timeStamp ← System.GetClockPulses[];
ENDCASE => {RemoveTranslate[xlate, e]; CommHeap.zone.FREE[@e]};
END;
pending =>
BEGIN
SELECT TRUE FROM
(age < retryPulses) => NULL;
((e.tries ← e.tries + 1) > retryLimit) => e.status ← zombie;
ENDCASE => {SendRequest[]; e.timeStamp ← System.GetClockPulses[]};
pendingEntries ← TRUE;
END;
new =>
BEGIN
e.status ← pending;
SendRequest[]; e.timeStamp ← System.GetClockPulses[];
pendingEntries ← TRUE;
END;
ENDCASE => IF CommFlags.doDebug THEN ERROR;
ENDLOOP; --end of queue entries loop
IF ~pendingEntries THEN Process.DisableTimeout[@xlate.event]
ELSE Process.SetTimeout[@xlate.event, Process.SecondsToTicks[1]];
ENDLOOP;
FOR e ← xlate.head, nextE UNTIL e = NIL DO
nextE ← e.nextLink; CommHeap.zone.FREE[@e]; e ← nextE; ENDLOOP;
xlate.head ← xlate.me ← xlate.all ← NIL;
END; --Demon
ReceiveTranslate: PROC[b: Buffer.Buffer] =
BEGIN
OPEN context: LOOPHOLE[b.context, PupRouterDefs.NetworkContext];
xlate: Handle = context.protocol;
SELECT b.rawWords[0] FROM
translationRequest =>
BEGIN
a: Buffer.Buffer;
requesterAddr: TranslatePair;
request: TranslatePair ← LOOPHOLE[@b.rawWords[1]];
request ← LOOPHOLE[@b.rawWords[1]];
IF request.pupHost = xlate.me.translatePair.pupHost THEN
BEGIN
--since the requester is probably going to talk to us,
--add his address before we take a fault
requesterAddr ← request + SIZE[TranslateRecord];
[] ← AddTranslateRecord[xlate, requesterAddr↑];
request.nsAddr ← xlate.me.translatePair.nsAddr;
--send the ack back
a ← Buffer.GetBuffer[, PupPktOps.pupBuffers, send, smallBuffer, FALSE];
IF a = NIL THEN RETURN; --nothing going to happen here
a.encapsulation ← [
ethernet[
ethernetDest: b.encapsulation.ethernetSource,
ethernetSource: myNSHost, ethernetType: translation]];
a.driver.length ← SIZE[DriverTypes.Encapsulation] +
SIZE[TranslateRecord] + 1;
a.rawWords[0] ← translationResponse;
LOOPHOLE[@a.rawWords[1], TranslatePair]↑ ← request↑;
-- UNIX checks to see if the answer is "for it"
-- MIN length roundup sends the extra bits
LOOPHOLE[@a.rawWords[1+SIZE[TranslateRecord]], TranslatePair]↑ ←
requesterAddr↑;
NARROW[b.network, Network].sendRawBuffer[a];
END;
END;
translationResponse =>
BEGIN
IF b.encapsulation.ethernetDest = xlate.me.translatePair.nsAddr THEN
[] ← AddTranslateRecord[xlate,
LOOPHOLE[@b.rawWords[1], TranslatePair]↑];
END;
ENDCASE;
END; --ReceiveTranslate
-- NB: There aren't any reasonable values of PupHostID to reserve for starting/stopping
-- So this routine is actually just a lookup.
-- If the entry doesn't exist (or is dead) nullHostNumber is returned.
EnumerateTranslation: PUBLIC ENTRY PROC [
old: PupTypes.PupHostID, network: Driver.Network]
RETURNS[new: PupTypes.PupHostID, translation: System.HostNumber] =
BEGIN
entry: TranslateEntry;
translation ← System.nullHostNumber;
new ← old;
FOR xlate: Handle ← chain, xlate.next UNTIL xlate = NIL DO
IF xlate.driver # network THEN LOOP;
entry ← FindTranslate[xlate, old];
IF entry # NIL AND entry.status = active THEN
translation ← entry.translatePair.nsAddr;
EXIT;
ENDLOOP
END;
END... --Pup10MBitImpl