-- MDSRegion.Mesa Edited by Sandman on August 18, 1980 10:30 AM -- Copyright Xerox Corporation 1979, 1980 DIRECTORY AllocDefs USING [SwappingProcedure, SwapStrategy], AltoDefs USING [PageCount, PageNumber, PageSize], ControlDefs USING [ GFT, GlobalFrameHandle, NullGlobalFrame, PrefixHandle, PrefixHeader], InlineDefs USING [BITOR], Map USING [Clean, Entry, SETF], NucleusOps USING [], ProcessDefs USING [DisableInterrupts, EnableInterrupts], Region USING [ Count, MaxRegionPage, Node, NodeObject, NodeSeal, Object, Page, PagesPerRegion, PageStatus], SDDefs USING [SD, sGFTLength], SegmentDefs USING [ AddressFromPage, AllocInfo, FileSegmentHandle, MemoryConfig, SegmentHandle], SwapperOps USING [BusyPage, FreePage, PageMap, PageTable]; MDSRegion: PROGRAM IMPORTS InlineDefs, Map, ProcessDefs, SegmentDefs EXPORTS AllocDefs, NucleusOps, SwapperOps, SegmentDefs SHARES SegmentDefs =PUBLIC BEGIN OPEN SegmentDefs, AltoDefs, SwapperOps, AllocDefs, Region; memConfig: PUBLIC MemoryConfig; -- MDS Region mdsNodes: NodeObject; pageTable: PageTable; map: PageMap ← @pageTable; ff, lf, rover, roverCopy: Region.Page; mdsRegion: Region.Object; strategyList: POINTER TO SwapStrategy; InvalidNode: ERROR [node: UNSPECIFIED] = CODE; Available: PROCEDURE [page: Region.Page, info: AllocInfo] RETURNS [available: BOOLEAN] = BEGIN seg: SegmentHandle; available ← FALSE; ProcessDefs.DisableInterrupts[]; SELECT (seg ← map[page]) 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: Region.Page] RETURNS [seg: SegmentHandle, status: PageStatus] = BEGIN ProcessDefs.DisableInterrupts[]; SELECT (seg ← map[page]) 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 ~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 n: Node ← @mdsNodes; up: BOOLEAN = info.direction = bottomup; DO n ← IF up THEN n.fwd ELSE n.back; IF n = @mdsNodes THEN GOTO noRoom; IF n # AddressFromPage[n.base] OR n.seal # Region.NodeSeal THEN ERROR InvalidNode[n]; SELECT n.pages FROM > pages => BEGIN IF ~up THEN base ← n.base + (n.pages ← n.pages - pages) ELSE BEGIN new: Node ← AddressFromPage[(base ← n.base) + pages]; new↑ ← [base: base + pages, pages: n.pages - pages, fwd: n.fwd, back: n.back]; new.back.fwd ← new; new.fwd.back ← new; END; EXIT; END; = pages => BEGIN base ← n.base; n.back.fwd ← n.fwd; n.fwd.back ← n.back; EXIT; END; ENDCASE; 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 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[]; mdsRegion.hole ← 0; FOR base IN [base..base + pages) DO map[base] ← seg; ENDLOOP; FOR n ← mdsNodes.fwd, n.fwd UNTIL n = @mdsNodes DO IF n # AddressFromPage[n.base] OR n.seal # Region.NodeSeal THEN ERROR InvalidNode[n]; mdsRegion.hole ← MAX[mdsRegion.hole, n.pages]; ENDLOOP; ProcessDefs.EnableInterrupts[]; END ELSE IF seg = FreePage THEN BEGIN n, n1: Node; page: Page; ProcessDefs.DisableInterrupts[]; FOR page IN [base..base + pages) DO IF map[page] = FreePage THEN ERROR; map[page] ← FreePage; ENDLOOP; -- Validate nodes FOR n ← mdsNodes.fwd, n.fwd UNTIL n = @mdsNodes DO IF n # AddressFromPage[n.base] OR n.seal # Region.NodeSeal THEN ERROR InvalidNode[n]; ENDLOOP; -- enter new node in order -- R: n.back.base < base < n.base OR n.back = @mdsNodes; -- P: base < n.base; mdsNodes.base = 177777B n ← @mdsNodes; DO n1 ← n.back; IF n1 = @mdsNodes OR n1.base < base THEN EXIT; n ← n1; ENDLOOP; n1 ← AddressFromPage[base]; n1↑ ← [base: base, pages: pages, fwd: n, back: n.back]; n.back.fwd ← n1; n.back ← n1; mdsRegion.hole ← MAX[mdsRegion.hole, pages]; n ← mdsNodes.fwd; DO n1 ← n.fwd; IF n.base + n.pages = n1.base THEN BEGIN mdsRegion.hole ← MAX[n.pages ← n.pages + n1.pages, mdsRegion.hole]; n.fwd ← n1.fwd; n1.fwd.back ← n; END ELSE n ← n1; IF n = @mdsNodes THEN EXIT; ENDLOOP; ProcessDefs.EnableInterrupts[]; END ELSE BEGIN newFree: BOOLEAN ← FALSE; ProcessDefs.DisableInterrupts[]; FOR base IN [base..base + pages) DO IF map[base] = FreePage THEN newFree ← TRUE; map[base] ← seg; ENDLOOP; IF newFree THEN BEGIN state: {free, busy} ← busy; mdsNodes.fwd ← mdsNodes.back ← @mdsNodes; mdsRegion.hole ← 0; FOR base DECREASING IN [0..MaxRegionPage] DO IF map[base] = FreePage THEN IF state = free THEN pages ← pages + 1 ELSE BEGIN state ← free; pages ← 1; END ELSE IF state = free THEN BEGIN new: Node ← AddressFromPage[base + 1]; new↑ ← [base: base + 1, pages: pages, fwd: mdsNodes.fwd, back: @mdsNodes]; new.fwd.back ← new; mdsNodes.fwd ← new; mdsRegion.hole ← MAX[mdsRegion.hole, pages]; state ← busy; END; ENDLOOP; END; ProcessDefs.EnableInterrupts[]; END; RETURN END; trySwapInProgress: BOOLEAN ← FALSE; TrySwapping: SwappingProcedure = BEGIN did: BOOLEAN; sp, next: POINTER TO SwapStrategy; ProcessDefs.DisableInterrupts[]; IF trySwapInProgress THEN BEGIN ProcessDefs.EnableInterrupts[]; RETURN[TryCodeSwapping[needed, info, seg]]; END; trySwapInProgress ← TRUE; ProcessDefs.EnableInterrupts[]; did ← TRUE; FOR sp ← strategyList, next UNTIL sp = NIL DO next ← sp.link; IF sp.proc[needed, info, seg] THEN EXIT; REPEAT FINISHED => did ← FALSE; ENDLOOP; trySwapInProgress ← FALSE; RETURN[did] END; CantSwap: SwappingProcedure = BEGIN RETURN[FALSE] END; TryCodeSwapping: SwappingProcedure = BEGIN OPEN ControlDefs; foundHole: BOOLEAN; readMap: BOOLEAN = memConfig.AltoType = D0 OR memConfig.AltoType = Dorado; pass: {first, second, quit} ← first; okay: BOOLEAN; base, page: Page; segment: SegmentHandle; status: PageStatus; n, inc: PageCount; page ← n ← 0; ProcessDefs.DisableInterrupts[]; segment ← Status[roverCopy ← rover].seg; IF segment # NIL THEN rover ← segment.VMpage MOD PagesPerRegion; base ← rover; DO -- until we've looked at them all twice [segment, status] ← Status[rover]; okay ← FALSE; SELECT (segment ← map[rover]) FROM BusyPage => inc ← 1; FreePage => {okay ← TRUE; inc ← 1}; ENDCASE => WITH s: segment SELECT FROM data => inc ← s.pages; file => BEGIN IF s.lock = 0 AND ~s.write AND ~s.busy 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: PrefixHandle ← AddressFromPage[s.VMpage]; info: CARDINAL ← p.header.swapinfo; IF info > 1 THEN BEGIN p ← p + info; info ← p.header.swapinfo; END; IF info = 0 THEN okay ← TRUE ELSE p.header.swapinfo ← 0; END ELSE IF s.inuse THEN s.inuse ← FALSE ELSE okay ← TRUE; END; inc ← s.pages; END; ENDCASE; IF ~okay THEN BEGIN page ← n ← 0; IF pass = quit THEN BEGIN foundHole ← FALSE; EXIT END; 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; base ← rover ← page + n; WHILE page < base DO SELECT (segment ← map[page]) FROM BusyPage, FreePage => page ← page + 1; ENDCASE => IF segment # NIL THEN { relPage: Page = segment.VMpage MOD PagesPerRegion; WITH s: segment SELECT FROM data => page ← relPage + s.pages; file => { IF s.lock = 0 THEN { IF s.class = code THEN UpdateCodebases[@s]; IF ~s.write THEN { s.swappedin ← FALSE; s.file.swapcount ← s.file.swapcount - 1}; Update[relPage, s.pages, FreePage, FALSE]}; page ← relPage + s.pages}; ENDCASE}; ENDLOOP; ProcessDefs.EnableInterrupts[]; RETURN[foundHole] END; SwapOutUnlocked: PUBLIC PROCEDURE [seg: FileSegmentHandle] = BEGIN ProcessDefs.DisableInterrupts[]; seg.swappedin ← FALSE; seg.file.swapcount ← seg.file.swapcount - 1; ProcessDefs.EnableInterrupts[]; END; UpdateCodebases: PUBLIC PROCEDURE [seg: FileSegmentHandle] = BEGIN OPEN ControlDefs; lastUser, f: ControlDefs.GlobalFrameHandle; nUsers, i: CARDINAL; epbase: CARDINAL; segBase: PageNumber; vmpage: PageNumber; ProcessDefs.DisableInterrupts[]; nUsers ← 0; FOR i IN [1..SDDefs.SD[SDDefs.sGFTLength]) DO [frame: f, epbase: epbase] ← GFT[i]; IF f # NullGlobalFrame AND epbase = 0 THEN BEGIN IF ~f.code.out THEN BEGIN vmpage ← f.code.offset/AltoDefs.PageSize; IF f.code.highByte = 0 THEN vmpage ← vmpage + PagesPerRegion*f.code.otherByte; segBase ← seg.VMpage; IF vmpage ~IN [segBase..segBase + seg.pages) THEN LOOP; f.code.offset ← f.code.offset - AltoDefs.PageSize*segBase; f.code.handle ← LOOPHOLE[seg]; f.code.out ← TRUE; END ELSE IF f.code.handle # LOOPHOLE[seg] THEN LOOP; IF ~f.shared THEN EXIT; nUsers ← nUsers + 1; lastUser ← f; END; REPEAT FINISHED => IF nUsers = 1 THEN lastUser.shared ← FALSE; ENDLOOP; ProcessDefs.EnableInterrupts[]; RETURN END; END.....