-- Filer>SubVolumeImpl.mesa (last edited by Luniewski on February 5, 1981  6:41 PM)

--Things to consider: 
--1) Taking subvolumes offline needs more synchronization. Can probably cover subvolumes with filecache reference counts since holder of a SubVolume.Handle usually holds a FilePtr for a file on the subvolume. Offline must then flush file cache before removing subvolume (allows pending I/O to complete).

DIRECTORY
  DiskChannel USING [
    Address, Command, CompletionHandle, CreateCompletionObject, GetPageAddress,
    Handle, --Idle,--InitiateIO, IORequestHandle, nullHandle, --Restart,--
    WaitAny],
  Environment USING [PageNumber],
  File USING [ID, PageCount, PageNumber],
  FileInternal USING [Operation],
  FilePageLabel USING [Label, nullLabel, SetFilePage, SetType],
  FilerPrograms,
  FileTask USING [DiskFinish, DiskStart],
  PhysicalVolumeFormat USING [SubVolumeDesc],
  Process USING [GetPriority, Priority, SetPriority],
  ProcessPriorities USING [priorityPageFaultHigh],
  RuntimeInternal USING [WorryCallDebugger],
  SubVolume USING [Descriptor, Handle, IOptr],
  Volume USING [ID],
  VolumeInternal USING [PageNumber];

SubVolumeImpl: MONITOR
  IMPORTS DiskChannel, FilePageLabel, FileTask, Process, RuntimeInternal
  EXPORTS SubVolume, FilerPrograms =
  BEGIN OPEN DiskChannel; -- Preliminary version of subvolume cache

  -- Gasp! each opened physical Volume takes one cacheEntry
  -- plus 1 for each real subvolume.
  cacheSize: CARDINAL = 12;
  CacheIndex: TYPE = [1..cacheSize];
  CacheEntry: TYPE = RECORD [occupied: BOOLEAN, svDesc: SubVolume.Descriptor];
  CePtr: TYPE = ORDERED POINTER TO CacheEntry;

  ceFirst: CePtr = LOOPHOLE[@cache[FIRST[CacheIndex]]]; -- loophole for Ordered
  ceLast: CePtr = LOOPHOLE[@cache[LAST[CacheIndex]]];
  cache: ARRAY CacheIndex OF CacheEntry ← ALL[[FALSE, ]];

  completion: PUBLIC CompletionHandle;

  InitializeSubVolume: PROCEDURE [] =
    BEGIN
    throwAway: PROCESS;
    priorityPrev: Process.Priority;
    completion ← CreateCompletionObject[];
    priorityPrev ← Process.GetPriority[];
    Process.SetPriority[ProcessPriorities.priorityPageFaultHigh];
    throwAway ← FORK FinishIO[];  -- (no profit in Detaching)
    Process.SetPriority[priorityPrev];
    END;

  ErrorHalt: PROCEDURE [s: STRING] =  --Signals down here are bad--
    BEGIN RuntimeInternal.WorryCallDebugger[s] END;

  Find: PUBLIC ENTRY PROCEDURE [vID: Volume.ID, page: VolumeInternal.PageNumber]
    RETURNS [success: BOOLEAN, subVolume: SubVolume.Handle] =
    BEGIN [success, subVolume] ← FindSV[vID, page]; END;

  FindSV: INTERNAL PROCEDURE [vID: Volume.ID, page: VolumeInternal.PageNumber]
    RETURNS [BOOLEAN, SubVolume.Handle] = INLINE
    BEGIN
    cePtr: CePtr;
    FOR cePtr ← ceFirst, cePtr + SIZE[CacheEntry] WHILE cePtr <= ceLast DO
      IF cePtr.occupied AND vID = cePtr.svDesc.lvID AND
          page IN [cePtr.svDesc.lvPage..cePtr.svDesc.lvPage + cePtr.svDesc.nPages) THEN
         RETURN[TRUE, @cePtr.svDesc];
      ENDLOOP;
    RETURN[FALSE, NIL]
    END;

  GetNext: PUBLIC ENTRY PROCEDURE [inSubVolume: SubVolume.Handle]
    RETURNS [outSubVolume: SubVolume.Handle] =
    BEGIN
    cePtr: CePtr;
    found: BOOLEAN ← inSubVolume = NIL;
    FOR cePtr ← ceFirst, cePtr + SIZE[CacheEntry] WHILE cePtr <= ceLast DO
      IF found THEN {IF cePtr.occupied THEN RETURN[@cePtr.svDesc]}
      ELSE found ← (inSubVolume = @cePtr.svDesc);
      ENDLOOP;
    IF ~found THEN ErrorHalt["illegalSubvolumeHandle"L];
    RETURN[NIL]
    END;

  GetPageAddress: PUBLIC ENTRY PROCEDURE [
    vID: Volume.ID, page: VolumeInternal.PageNumber]
    RETURNS [channel: DiskChannel.Handle, address: DiskChannel.Address] =
    BEGIN
    found: BOOLEAN;
    svH: SubVolume.Handle;
    [found, svH] ← FindSV[vID, page];
    IF NOT found THEN ErrorHalt["getPageAddressFailure"L];
    RETURN[
      svH.channel,
      DiskChannel.GetPageAddress[svH.channel, svH.pvPage + (page - svH.lvPage)]]
    END;

  OnLine: PUBLIC ENTRY PROCEDURE [
    subVolume: PhysicalVolumeFormat.SubVolumeDesc, channel: DiskChannel.Handle] =
    BEGIN OPEN subVolume;
    cePtr: CePtr;
    FOR cePtr ← ceFirst, cePtr + SIZE[CacheEntry] WHILE cePtr <= ceLast DO
      IF ~cePtr.occupied THEN
         BEGIN
         cePtr↑ ←
           [occupied: TRUE,
             svDesc:
             [lvID: lvID, lvPage: lvPage, pvPage: pvPage, nPages: nPages,
               channel: channel]];
         RETURN;
         END;
      ENDLOOP;
    ErrorHalt["subvolume cache overflow"L];
    END;

  -- Flush Subvolume; (null Handle flushes all subvolumes of a given logical volume)

  OffLine: PUBLIC ENTRY PROCEDURE [vID: Volume.ID, channel: DiskChannel.Handle] =
    BEGIN
    cePtr: CePtr;
    FOR cePtr ← ceFirst, cePtr + SIZE[CacheEntry] WHILE cePtr <= ceLast DO
      OPEN cePtr;
      IF svDesc.lvID = vID AND
          (channel = DiskChannel.nullHandle OR channel = svDesc.channel) THEN
         occupied ← FALSE;
      ENDLOOP;
    END;

--Acquire: PUBLIC ENTRY PROCEDURE =
  --BEGIN ++ Idles all Pilot subvolumes to allow Alto file I/O
  --cePtr: CePtr;
  --FOR cePtr ← ceFirst, cePtr+SIZE[CacheEntry] WHILE cePtr <= ceLast
  --DO IF cePtr.occupied THEN Idle[cePtr.svDesc.channel]; ENDLOOP;
  --END;
  --Release: PUBLIC ENTRY PROCEDURE =
  --BEGIN ++ Restarts all Pilot subvolumes when Alto file I/O is completed
  --cePtr: CePtr;
  --FOR cePtr ← ceFirst, cePtr+SIZE[CacheEntry] WHILE cePtr <= ceLast
  --DO IF cePtr.occupied THEN Restart[cePtr.svDesc.channel]; ENDLOOP;
  --END;

  StartIO: PUBLIC --ENTRY--PROCEDURE [io: SubVolume.IOptr] =
    BEGIN
    labelSize: CARDINAL = SIZE[FilePageLabel.Label];
    ErrorHalt1: PROCEDURE RETURNS [DiskChannel.Command] = LOOPHOLE[ErrorHalt];
    requestHandle: IORequestHandle;
    properLabel: FilePageLabel.Label ← FilePageLabel.nullLabel;
    properLabel.fileID ← io.filePtr.fileID;
    FilePageLabel.SetFilePage[@properLabel, io.filePage];
    IF io.chained THEN properLabel.bootChainLink ← LOOPHOLE[io.link];
    WITH io.filePtr SELECT FROM -- couldn't his be done at a higher level???
      local =>
         BEGIN
         FilePageLabel.SetType[@properLabel, type];
         IF io.filePage = 0 THEN
            BEGIN
            properLabel.immutable ← immutable;
            properLabel.temporary ← temporary;
            properLabel.zeroSize ← (size = 0);
            END;
         END;
      ENDCASE => ErrorHalt["can't do label operations on remote files"L];
    requestHandle ← FileTask.DiskStart[io.memPage, io.pageCount, io.filePtr, io.op];
    BEGIN OPEN requestHandle;
    channel ← io.subVolume.channel;
    diskPage ← io.subVolume.pvPage + io.subVolumePage;
    label↑ ← LOOPHOLE[properLabel];
    memoryPage ← io.memPage;
    count ← io.pageCount;
    dontIncrement ← io.fixedMemPage;
    command ←
      SELECT io.op FROM
         read => vvr,
         write => vvw,
         readLabel => vrr,
         writeLabel => vww,
         verifyLabel => vvr,
         readLabelAndData => vrr,
         writeLabelsAndData => vww,
         ENDCASE => ErrorHalt1[];
    END;
    InitiateIO[requestHandle];
    END;

  FinishIO: PROCEDURE = -- root of Finisher process
    BEGIN
    requestHandle: IORequestHandle;
    labelValid: BOOLEAN ← TRUE;
    hardErrorString: STRING ← "Unrecoverable disk error page XXXXX";
    DO
      requestHandle ← WaitAny[completion];
      FileTask.DiskFinish[requestHandle];
      ENDLOOP;
    END;

  InitializeSubVolume[];

  END.

LOG
February 22, 1979  4:33 PM   Redell   Created file from old DiskImpl and VolumeCacheImpl.
March 21, 1979  6:00 PM   Redell   Converted to Mesa 5.0.
March 26, 1979  4:50 PM   Redell   Fixed typo: write => put in StartIO.
July 30, 1979  10:44 AM   Forrest   Made compile with new subvolume.  Upped cache to 8.
August 13, 1979  6:32 PM   Redell   Added bootchain machinery; will be modified when disk driver is changed to accept runs of pages.
September 10, 1979  6:29 PM   Forrest   Use Pilot file types vs fileTypes.
September 19, 1979  8:54 AM   Forrest   Add GetNext; Change Offline to also take channel Handle.
September 21, 1979  11:43 AM   Forrest   Add Set/GetMarker ID.
September 22, 1979  2:44 PM    Forrest   took out Set/GetMarker ID.
November 26, 1979  3:45 PM    Gobbel   Changes for new DiskChannel (runs of pages).
December 13, 1979  7:20 PM    Gobbel   Added fixedMemPage option, make parameter to StartIO be handle on a record.
January 22, 1980  1:38 PM    Gobbel   Tell disk page number when we go to the debugger for a hard error.
January 30, 1980  3:30 PM    Gobbel   Remove Alto file stuff.
May 20, 1980  3:04 PM    Luniewski   PhysicalVolume => PhysicalVolumeFormat.
January 9, 1981  4:15 PM    Luniewski   Move error handling on I/O completion to FileTask.
January 20, 1981  1:35 PM    Knutsen   Change process priority.
February 5, 1981  6:41 PM    Luniewski   FineInternal.FilePtr no longer needed.