-- MarkerPageImpl.mesa (last edited by: Luniewski on: January 12, 1981  11:05 AM)
-- It is not clear how much checking to do when something comes on line.  For the time being, We do no real checking, assuming that marker pages are set up right.  This may be the correct philosophy, since marker pages are copies of the truth for the scavenger.

DIRECTORY
  DiskChannel USING [Drive, GetAttributes, nullDrive],
  File USING [ID, nullID],
  FMPrograms USING [],
  FileInternal USING [maxPermissions],
  LabelTransfer USING [WriteLabels],
  LogicalVolume USING [Handle, LogicalSubvolumeMarker],
  MarkerPage USING [CacheKey, SubVolumeMarkerPage],
  PhysicalVolume USING [Error, ErrorType, ID, nullID],
  PhysicalVolumeFormat USING [Handle, PageNumber, PhysicalSubvolumeMarker],
  PilotFileTypes USING [tSubVolumeMarkerPage],
  SimpleSpace USING [Create, Handle, Map, Page, Unmap],
  SubVolume USING [Find, Handle],
  Utilities USING [LongPointerFromPage];

MarkerPageImpl: MONITOR
  IMPORTS
    DiskChannel, LabelTransfer, PhysicalVolume, SimpleSpace, SubVolume, Utilities
  EXPORTS FMPrograms, MarkerPage
  SHARES File =
  BEGIN
  -- Buffer variables for accessing marker pages 
  markerPageBuffer: SimpleSpace.Handle = SimpleSpace.Create[1, hyperspace];
  marker: LONG POINTER TO MarkerPage.SubVolumeMarkerPage =
    Utilities.LongPointerFromPage[SimpleSpace.Page[markerPageBuffer]];
  cacheSize: CARDINAL = 5; -- should be one for each possible disk
  cache: ARRAY [0..cacheSize) OF RECORD [
    occupied: BOOLEAN,
    drive: DiskChannel.Drive,
    physicalID: PhysicalVolume.ID,
    markerID: File.ID] ← ALL[[FALSE, , , ]];
  NotFound: PUBLIC ERROR = CODE;
  MarkerPageError: ERROR [
    {cacheFull, impossibleCacheVarient, subvolumeNotFound}] = CODE;

  -- Create the marker page for the i'th subvolume.  The ID is in the cache, and the page groups have been set up properly

  CreateMarkerPage: PUBLIC ENTRY PROCEDURE [
    pvHandle: PhysicalVolumeFormat.Handle, lvHandle: LogicalVolume.Handle,
    physicalSubvolumeNumber: CARDINAL] =
    BEGIN OPEN sv: pvHandle.subVolumes[physicalSubvolumeNumber];
    page: LONG CARDINAL ← sv.pvPage + sv.nPages;
    [] ← LabelTransfer.WriteLabels[
      file: [FindMarkerID[[physicalID[pvHandle.pvID]]], LOOPHOLE[pvHandle.pvID],
            local[FALSE, FALSE, page --Should be driveSize--,
                   PilotFileTypes.tSubVolumeMarkerPage]],
      pageGroup: [page, page, page + 1]];
    MarkerPageMap[FindMarkerID[[physicalID[pvHandle.pvID]]], page];
    marker↑ ←
      [physical: [pvID: pvHandle.pvID, label: pvHandle.label,
          bootingInfo: pvHandle.bootingInfo, maxBadPages: pvHandle.maxBadPages,
          labelLength: pvHandle.labelLength, svNumber: physicalSubvolumeNumber,
              descriptor: sv],
       logical: [labelLength: lvHandle.labelLength, type: lvHandle.type,
          label: lvHandle.label, bootingInfo: lvHandle.bootingInfo,
          clientRootFile: lvHandle.clientRootFile]];
    MarkerPageUnmap[];
    END;

  Enter: PUBLIC ENTRY PROCEDURE [
    drive: DiskChannel.Drive, physicalID: PhysicalVolume.ID] =
    BEGIN
    i: CARDINAL;
    FOR i IN [0..LENGTH[cache]) DO
      IF drive = cache[i].drive THEN
         { cache[i] ← [TRUE, drive, physicalID, File.nullID]; RETURN; };
      ENDLOOP;
    FOR i IN [0..LENGTH[cache]) DO
      IF ~cache[i].occupied THEN
        { cache[i] ← [TRUE, drive, physicalID, File.nullID]; RETURN; };
      ENDLOOP;
    MarkerPageError[cacheFull];
    END;

  EnterMarkerID: PUBLIC ENTRY PROCEDURE [
    c: MarkerPage.CacheKey, markerID: File.ID] =
    BEGIN
    ENABLE UNWIND => NULL;
    i: CARDINAL ← FindInternal[c];
    cache[i].markerID ← markerID;
    END;

  -- Find is exported because some PhysicalVolume operations use it.

  Find: PUBLIC ENTRY PROCEDURE [c: MarkerPage.CacheKey]
    RETURNS [
      drive: DiskChannel.Drive, physicalID: POINTER TO READONLY PhysicalVolume.ID,
      markerID: POINTER TO READONLY File.ID] =
    BEGIN
    ENABLE UNWIND => NULL;
    i: CARDINAL ← FindInternal[c];
    RETURN[cache[i].drive, @cache[i].physicalID, @cache[i].markerID];
    END;

  Flush: PUBLIC ENTRY PROCEDURE [c: MarkerPage.CacheKey] =
    BEGIN
    ENABLE UNWIND => NULL;
    i: CARDINAL ← FindInternal[c];
    cache[i].occupied ← FALSE;
    END;

  GetNextPhysicalVolume: PUBLIC ENTRY PROCEDURE [thisPvID: PhysicalVolume.ID]
    RETURNS [nextPvID: PhysicalVolume.ID, drive: DiskChannel.Drive] =
    BEGIN
    i: CARDINAL;
    found: BOOLEAN ← (thisPvID = PhysicalVolume.nullID);
    FOR i IN [0..LENGTH[cache]) DO
      IF cache[i].occupied THEN
         IF found THEN RETURN[LOOPHOLE[cache[i].physicalID], cache[i].drive]
         ELSE found ← (thisPvID = LOOPHOLE[cache[i].physicalID]);
      ENDLOOP;
    IF found THEN RETURN[PhysicalVolume.nullID, DiskChannel.nullDrive]
    ELSE RETURN WITH ERROR PhysicalVolume.Error[physicalVolumeUnknown];
    END;

  UpdateLogicalMarkerPages: PUBLIC ENTRY PROCEDURE [
    lvHandle: LogicalVolume.Handle] =
    BEGIN
    svH: SubVolume.Handle ← NIL;
    lpage: LONG CARDINAL ← 0;
    found: BOOLEAN;
    WHILE lpage < lvHandle.volumeSize DO
      [found, svH] ← SubVolume.Find[lvHandle.vID, lpage];
      IF ~found THEN ERROR MarkerPageError[subvolumeNotFound];
      MarkerPageMap[
         FindMarkerID[[drive[DiskChannel.GetAttributes[svH.channel].drive]]],
         svH.pvPage + svH.nPages];
      marker.logical.bootingInfo ← lvHandle.bootingInfo;
      marker.logical.clientRootFile ← lvHandle.clientRootFile;
      MarkerPageUnmap[];
      lpage ← svH.lvPage + svH.nPages;
      ENDLOOP;
    END;

  UpdatePhysicalMarkerPages: PUBLIC ENTRY PROCEDURE [
    pvHandle: PhysicalVolumeFormat.Handle] =
    BEGIN
    i: CARDINAL;
    FOR i IN [0..pvHandle.subVolumeCount) DO
      OPEN sv: pvHandle.subVolumes[i];
      MarkerPageMap[
         FindMarkerID[[physicalID[pvHandle.pvID]]], sv.pvPage + sv.nPages];
      marker.physical.bootingInfo ← pvHandle.bootingInfo;
      MarkerPageUnmap[];
      ENDLOOP;
    END; -- Internal Procedures

  FindInternal: INTERNAL PROCEDURE [c: MarkerPage.CacheKey]
    RETURNS [i: CARDINAL] =
    BEGIN
    FOR i IN [0..LENGTH[cache]) DO
      IF cache[i].occupied THEN
         WITH c SELECT FROM
            drive => IF h = cache[i].drive THEN RETURN;
            physicalID => IF id = cache[i].physicalID THEN RETURN;
            ENDCASE => ERROR MarkerPageError[impossibleCacheVarient];
      ENDLOOP;
    ERROR NotFound;
    END;

  FindMarkerID: INTERNAL PROCEDURE [c: MarkerPage.CacheKey] RETURNS [File.ID] =
    INLINE { RETURN[cache[FindInternal[c]].markerID]; };

  MarkerPageMap: INTERNAL PROCEDURE [ID: File.ID, page: LONG CARDINAL] =
    BEGIN
    SimpleSpace.Map[
      markerPageBuffer, [[ID, FileInternal.maxPermissions], page], FALSE];
    END;

  MarkerPageUnmap: INTERNAL PROCEDURE =
    { SimpleSpace.Unmap[markerPageBuffer]; };

  END.....
LOG
Time: October 1, 1979  12:20 PM   By: Forrest   Action: Created from volumeImpl.mesa
Time: October 16, 1979  12:44 PM   By: Forrest   Action: Fix bug in Update SVM pages
Time: May 20, 1980  4:14 PM   By: Luniewski   Action: PhysicalVolume => PhysicalVolumeFormat
Time: July 24, 1980  9:14 AM   By: Luniewski   Action: Deleted Export to SpecialVolume.  Made GetNextPhysicalVolume export to MarkerPage.
Time: September 17, 1980  2:44 PM   By: Luniewski   Action: Added maxBadpages to marker page.
Time: January 12, 1981  11:05 AM   By: Luniewski   Action: Changes for new LabelTransfer interface.