-- HyperRegion.Mesa Edited by Sandman on October 8, 1980 4:05 PM
-- Copyright Xerox Corporation 1979, 1980
DIRECTORY
AllocDefs USING [SwappingProcedure],
InlineDefs USING [BITOR],
Map USING [Clean, Entry, SETF, VacantFlags],
Mopcodes USING [zRBL, zWBL],
NucleusOps USING [],
ProcessDefs USING [DisableInterrupts, EnableInterrupts],
Region USING [
Count, Handle, Index, MaxRegionPage, Node, Object, Page, PagesPerRegion,
PageStatus],
SegmentDefs USING [
AddressFromPage, AllocInfo, DataSegmentHandle, FileSegmentHandle, memConfig,
Object, PageCount, PageNumber, SegmentHandle, SwapOut, TableDS],
SwapperOps USING [
AllocateObject, BusyPage, FreePage, LiberateObject, Page, PageStatus,
SwapOutCodeSeg, SwapOutUnlocked, UpdateCodebases];
HyperRegion: PROGRAM [index: Region.Index] RETURNS [Region.Handle]
IMPORTS InlineDefs, Map, ProcessDefs, SegmentDefs, SwapperOps
EXPORTS NucleusOps, Region, SegmentDefs
SHARES SegmentDefs =
BEGIN OPEN Region, SegmentDefs, SwapperOps;
disabled: BOOLEAN;
freeList: Node;
ff, lf, rover: Page;
mapSegment: DataSegmentHandle;
myRegion: Region.Object;
ReadPrefixInfo: PROCEDURE [p: POINTER, region: Index ← index]
RETURNS [CARDINAL] = MACHINE CODE BEGIN Mopcodes.zRBL, 0 END;
WritePrefixInfo: PROCEDURE [info: CARDINAL, p: POINTER, region: Index ← index] =
MACHINE CODE BEGIN Mopcodes.zWBL, 0 END;
ReadMap: PROCEDURE [page: Page, region: Index ← index] RETURNS [SegmentHandle] =
MACHINE CODE BEGIN Mopcodes.zRBL, 0 END;
WriteMap: PROCEDURE [seg: SegmentHandle, page: Page, region: Index ← index] =
MACHINE CODE BEGIN Mopcodes.zWBL, 0 END;
ReadBase: PROCEDURE [node: Node, region: Index ← index] RETURNS [CARDINAL] =
MACHINE CODE BEGIN Mopcodes.zRBL, 0 END;
WriteBase: PROCEDURE [base: CARDINAL, node: Node, region: Index ← index] =
MACHINE CODE BEGIN Mopcodes.zWBL, 0 END;
ReadPages: PROCEDURE [node: Node, region: Index ← index] RETURNS [CARDINAL] =
MACHINE CODE BEGIN Mopcodes.zRBL, 1 END;
WritePages: PROCEDURE [pages: CARDINAL, node: Node, region: Index ← index] =
MACHINE CODE BEGIN Mopcodes.zWBL, 1 END;
ReadFwdLink: PROCEDURE [node: Node, region: Index ← index] RETURNS [Node] =
MACHINE CODE BEGIN Mopcodes.zRBL, 2 END;
WriteFwdLink: PROCEDURE [link: Node, node: Node, region: Index ← index] =
MACHINE CODE BEGIN Mopcodes.zWBL, 2 END;
ReadBackLink: PROCEDURE [node: Node, region: Index ← index] RETURNS [Node] =
MACHINE CODE BEGIN Mopcodes.zRBL, 3 END;
WriteBackLink: PROCEDURE [link: Node, node: Node, region: Index ← index] =
MACHINE CODE BEGIN Mopcodes.zWBL, 3 END;
Available: PROCEDURE [page: Page, info: AllocInfo]
RETURNS [available: BOOLEAN] =
BEGIN
seg: SegmentHandle;
available ← FALSE;
IF disabled THEN RETURN;
ProcessDefs.DisableInterrupts[];
seg ← ReadMap[page: page];
SELECT seg FROM
FreePage => available ← TRUE;
BusyPage => NULL;
ENDCASE =>
WITH s: seg SELECT FROM
file =>
IF info.effort = hard AND s.lock = 0 AND ~s.write AND ~s.busy THEN
available ← TRUE;
ENDCASE;
ProcessDefs.EnableInterrupts[];
RETURN
END;
Status: PROCEDURE [page: Page]
RETURNS [seg: SegmentHandle, status: PageStatus] =
BEGIN
IF disabled THEN RETURN[NIL, busy];
ProcessDefs.DisableInterrupts[];
seg ← ReadMap[page: page];
SELECT seg FROM
BusyPage => BEGIN status ← busy; seg ← NIL END;
FreePage => BEGIN status ← free; seg ← NIL END;
ENDCASE =>
IF seg.busy THEN BEGIN status ← busy; seg ← NIL; END ELSE status ← inuse;
ProcessDefs.EnableInterrupts[];
RETURN
END;
Alloc: PROCEDURE [
base: PageNumber, pages: Count, info: AllocInfo, anyWhere: BOOLEAN]
RETURNS [BOOLEAN, Page] =
BEGIN
vm: Page;
IF disabled THEN RETURN[FALSE, 0];
IF ~anyWhere THEN
BEGIN
ProcessDefs.DisableInterrupts[];
FOR vm IN [base..base + pages) DO
IF ~Available[vm, info] THEN GOTO noRoom; ENDLOOP;
END
ELSE
BEGIN
n: CARDINAL ← 0; -- count of contiguous free pages
direction: INTEGER;
ProcessDefs.DisableInterrupts[];
IF info.effort = hard THEN
BEGIN
IF info.direction = bottomup THEN BEGIN direction ← 1; base ← ff; END
ELSE BEGIN direction ← -1; base ← lf; END;
WHILE base IN [ff..lf] DO
IF ~Available[base, info] THEN n ← 0
ELSE
IF (n ← n + 1) = pages THEN
BEGIN IF direction > 0 THEN base ← base - n + 1; EXIT END;
base ← base + direction
REPEAT FINISHED => GOTO noRoom;
ENDLOOP
END
ELSE
BEGIN
nodePages: CARDINAL;
n: Node ← freeList;
IF n = NIL THEN GOTO noRoom;
DO
nodePages ← ReadPages[node: n];
base ← ReadBase[node: n];
SELECT nodePages FROM
> pages =>
BEGIN
base ← base + (nodePages ← nodePages - pages);
WritePages[pages: nodePages, node: n];
EXIT;
END;
= pages =>
BEGIN
back: Node ← ReadBackLink[node: n];
fwd: Node ← ReadFwdLink[node: n];
IF back = n THEN freeList ← NIL
ELSE
BEGIN
IF freeList = n THEN freeList ← back;
WriteBackLink[link: back, node: fwd];
WriteFwdLink[link: fwd, node: back];
END;
EXIT;
END;
ENDCASE;
n ← ReadFwdLink[node: n];
IF n = freeList THEN GOTO noRoom;
ENDLOOP;
Update[base, pages, BusyPage, TRUE];
ProcessDefs.EnableInterrupts[];
RETURN[TRUE, base]
END;
END;
FOR vm IN [base..base + pages) DO
BEGIN
tempseg: SegmentHandle ← Status[vm].seg;
IF tempseg # NIL THEN
WITH s: tempseg SELECT FROM
file =>
BEGIN
Update[s.VMpage MOD PagesPerRegion, s.pages, FreePage, FALSE];
IF s.class = code THEN UpdateCodebases[@s];
SwapOutUnlocked[@s];
END;
ENDCASE;
END;
ENDLOOP;
Update[base, pages, BusyPage, FALSE];
ProcessDefs.EnableInterrupts[];
RETURN[TRUE, base]
EXITS noRoom => BEGIN ProcessDefs.EnableInterrupts[]; RETURN[FALSE, 0] END;
END;
Update: PROCEDURE [
base: Page, pages: Count, seg: SegmentHandle, simple: BOOLEAN] =
BEGIN
IF disabled THEN RETURN;
IF seg = FreePage THEN
BEGIN
ProcessDefs.DisableInterrupts[];
ff ← MIN[ff, base];
lf ← MAX[lf, base + pages - 1];
ProcessDefs.EnableInterrupts[];
END;
IF simple THEN
BEGIN
n: Node;
ProcessDefs.DisableInterrupts[];
myRegion.hole ← 0;
FOR base IN [base..base + pages) DO WriteMap[page: base, seg: seg]; ENDLOOP;
IF (n ← freeList) # NIL THEN
DO
myRegion.hole ← MAX[ReadPages[node: n], myRegion.hole];
n ← ReadFwdLink[node: n];
IF n = freeList THEN EXIT;
ENDLOOP;
ProcessDefs.EnableInterrupts[];
END
ELSE
IF seg = FreePage THEN
BEGIN
n: Node;
page: Page;
nodePages: CARDINAL;
ProcessDefs.DisableInterrupts[];
FOR page IN [base..base + pages) DO
IF ReadMap[page: page] = FreePage THEN ERROR;
WriteMap[page: page, seg: FreePage];
ENDLOOP;
AddNode[base: base, pages: pages];
n ← freeList;
DO
nodePages ← ReadPages[node: n];
IF (base ← ReadBase[node: n] + nodePages) <= MaxRegionPage AND ReadMap[
page: base] = FreePage THEN
BEGIN
oldNode: Node ← AddressFromPage[base];
back: Node ← ReadBackLink[node: oldNode];
fwd: Node ← ReadFwdLink[node: oldNode];
WriteBackLink[link: back, node: fwd];
WriteFwdLink[link: fwd, node: back];
IF freeList = oldNode THEN freeList ← n;
nodePages ← ReadPages[node: oldNode] + nodePages;
myRegion.hole ← MAX[nodePages, myRegion.hole];
WritePages[pages: nodePages, node: n];
END
ELSE {n ← ReadFwdLink[node: n]; IF n = freeList THEN EXIT};
ENDLOOP;
ProcessDefs.EnableInterrupts[];
END
ELSE
BEGIN
newFree: BOOLEAN ← FALSE;
ProcessDefs.DisableInterrupts[];
FOR base IN [base..base + pages) DO
IF ReadMap[page: base] = FreePage THEN newFree ← TRUE;
WriteMap[page: base, seg: seg];
ENDLOOP;
IF newFree THEN
BEGIN
state: {free, busy} ← busy;
freeList ← NIL;
myRegion.hole ← 0;
FOR base DECREASING IN [0..MaxRegionPage] DO
IF ReadMap[page: base] = FreePage THEN
IF state = free THEN pages ← pages + 1
ELSE BEGIN state ← free; pages ← 1; END
ELSE
IF state = free THEN
BEGIN AddNode[base: base + 1, pages: pages]; state ← busy END;
ENDLOOP;
END;
ProcessDefs.EnableInterrupts[];
END;
RETURN
END;
AddNode: PROCEDURE [base: Page, pages: Count] =
BEGIN
n: Node ← AddressFromPage[base];
WriteBase[base: base, node: n];
WritePages[pages: pages, node: n];
IF freeList = NIL THEN
BEGIN
freeList ← n;
WriteBackLink[link: n, node: n];
WriteFwdLink[link: n, node: n];
END
ELSE
BEGIN
fwd: Node ← ReadFwdLink[node: freeList];
WriteBackLink[link: n, node: fwd];
WriteFwdLink[link: fwd, node: n];
WriteBackLink[link: freeList, node: n];
WriteFwdLink[link: n, node: freeList];
END;
myRegion.hole ← MAX[myRegion.hole, pages];
END;
CodeSwap: AllocDefs.SwappingProcedure =
BEGIN
foundHole: BOOLEAN ← FALSE;
segment: SegmentHandle;
base, page: Page;
n, inc: PageCount;
readMap: BOOLEAN = memConfig.AltoType = D0 OR memConfig.AltoType = Dorado;
pass: {first, second, quit} ← first;
IF disabled THEN RETURN[FALSE];
page ← n ← 0;
ProcessDefs.DisableInterrupts[];
segment ← Status[rover].seg;
IF segment # NIL THEN rover ← segment.VMpage MOD PagesPerRegion;
base ← rover;
DO
-- until we've looked at them all twice
okay: BOOLEAN;
status: PageStatus;
[segment, status] ← Status[rover];
okay ← FALSE;
SELECT status FROM
inuse =>
WITH s: segment SELECT FROM
data => inc ← s.pages;
file =>
BEGIN
IF s.lock = 0 AND ~s.write THEN
BEGIN
IF readMap THEN {
t: Map.Entry ← Map.Clean;
FOR p: CARDINAL IN [s.VMpage..s.VMpage + s.pages) DO
t ← InlineDefs.BITOR[t, Map.SETF[p, Map.Clean]]; ENDLOOP;
IF ~t.flags.R THEN okay ← TRUE}
ELSE
IF s.class = code THEN
BEGIN
p: POINTER ← AddressFromPage[s.VMpage];
info: CARDINAL ← ReadPrefixInfo[p];
IF info > 1 THEN
BEGIN p ← p + info; info ← ReadPrefixInfo[p]; END;
IF info = 0 THEN okay ← TRUE ELSE WritePrefixInfo[0, p];
END
ELSE IF s.inuse THEN s.inuse ← FALSE ELSE okay ← TRUE;
END;
inc ← s.pages;
END;
ENDCASE;
ENDCASE => BEGIN IF status = free THEN okay ← TRUE; inc ← 1; END;
IF ~okay THEN {
page ← n ← 0; IF pass = quit THEN BEGIN foundHole ← FALSE; EXIT END}
ELSE
BEGIN
IF page = 0 THEN page ← rover;
IF (n ← n + inc) >= needed THEN BEGIN foundHole ← TRUE; EXIT END;
END;
IF (rover ← rover + inc) > MaxRegionPage THEN
IF pass = quit THEN BEGIN foundHole ← FALSE; EXIT END
ELSE BEGIN rover ← page ← 0; n ← 0; END;
IF rover = base THEN IF pass = first THEN pass ← second ELSE pass ← quit;
ENDLOOP;
rover ← base ← page + n;
IF rover > MaxRegionPage THEN rover ← 1;
WHILE page < base DO
segment ← Status[page].seg;
IF segment # NIL THEN
BEGIN
relPage: Page = segment.VMpage MOD PagesPerRegion;
WITH s: segment SELECT FROM
data => page ← relPage + s.pages;
file =>
BEGIN
IF s.lock = 0 THEN
BEGIN
IF s.class = code THEN UpdateCodebases[@s];
IF ~s.write THEN SwapOutUnlocked[@s];
Update[relPage, s.pages, FreePage, FALSE];
END;
page ← relPage + s.pages; -- cross jumped to data select arm
END;
ENDCASE;
END
ELSE page ← page + 1;
ENDLOOP;
ProcessDefs.EnableInterrupts[];
RETURN[foundHole]
END;
ImmovableSegmentInXM: PUBLIC SIGNAL [seg: SegmentHandle] = CODE;
Disable: PROCEDURE [abandon: BOOLEAN] =
BEGIN
page: Page ← 1;
seg: SegmentHandle;
IF disabled THEN RETURN;
disabled ← TRUE;
IF abandon THEN {LiberateObject[mapSegment]; RETURN};
WHILE page <= MaxRegionPage DO
seg ← ReadMap[page: page];
IF seg = BusyPage OR seg = FreePage THEN {page ← page + 1; LOOP};
WITH s: seg SELECT FROM
data => {SIGNAL ImmovableSegmentInXM[@s]; page ← page + s.pages};
file => {
IF s.lock # 0 THEN SIGNAL ImmovableSegmentInXM[@s]
ELSE IF s.class = code THEN SwapOutCodeSeg[@s] ELSE SwapOut[@s];
page ← page + s.pages};
ENDCASE;
ENDLOOP;
LiberateObject[mapSegment];
END;
Initialize: PROCEDURE RETURNS [Handle] =
BEGIN
page: Page;
myRegion ←
[basePage: index*PagesPerRegion, alloc: Alloc, update: Update,
available: Available, status: Status, swap: CodeSwap, disable: Disable,
hole:];
FOR page IN Page DO WriteMap[page: page, seg: FreePage]; ENDLOOP;
disabled ← FALSE;
freeList ← NIL;
rover ← ff ← 1;
IF memConfig.AltoType = AltoIIXM THEN
BEGIN
WriteMap[page: 376B, seg: BusyPage];
WriteMap[page: 377B, seg: BusyPage];
AddNode[base: 1, pages: lf ← PagesPerRegion - 3];
myRegion.hole ← lf;
END
ELSE
BEGIN
FOR page DECREASING IN [0..PagesPerRegion) DO
IF PageThere[myRegion.basePage + page] THEN EXIT;
WriteMap[page: page, seg: BusyPage];
ENDLOOP;
AddNode[base: 1, pages: lf ← page];
myRegion.hole ← lf;
END;
mapSegment ← LOOPHOLE[AllocateObject[SIZE[data segment SegmentDefs.Object]]];
mapSegment↑ ←
[busy: FALSE,
body: segment[
VMpage: myRegion.basePage, info: data[type: TableDS, pages: 1]]];
WriteMap[page: 0, seg: mapSegment];
RETURN[@myRegion]
END;
PageThere: PROCEDURE [page: CARDINAL] RETURNS [BOOLEAN] =
BEGIN
t: Map.Entry;
t ← Map.SETF[page, Map.Clean];
[] ← Map.SETF[page, t];
RETURN[t.flags # Map.VacantFlags];
END;
RETURN[Initialize[]];
END.....