-- DiskChannelImpl.mesa (last edited by: Yokota on: December 9, 1980 7:49 PM) DIRECTORY Device USING [nullType, Type], DiskChannel USING [ Address, CompletionHandle, DiskPageCount, DiskPageNumber, Drive, DriveState, DriveStatus, Handle, IORequestHandle, nullDrive, nullHandle], DiskChannelBackend USING [DriveHandle, DriveID], Environment USING [Base, first64K], PhysicalVolume USING [nullDeviceIndex], PilotDisk USING [Handle], Process USING [InitializeCondition, MsecToTicks, SetTimeout], ResidentHeap USING [FreeNode, MakeNode], RuntimeInternal USING [WorryCallDebugger], Zone USING [BlockSize, Status]; DiskChannelImpl: MONITOR IMPORTS Process, ResidentHeap, RuntimeInternal EXPORTS DiskChannel, DiskChannelBackend SHARES DiskChannel, DiskChannelBackend, PilotDisk = BEGIN -- Miscellaneous base: Environment.Base = Environment.first64K; ErrorHalt: PROC = { RuntimeInternal.WorryCallDebugger["Error in DiskChannelImpl"L]; }; driveObjectUnlocked: CONDITION; g: GlobalHandle ← MakeLP[SIZE[Globals]]; GlobalHandle: TYPE = LONG POINTER TO Globals; Globals: TYPE = RECORD [ firstDrive: DiskChannelBackend.DriveHandle, -- head of the linked list of known drives channelRestarted: CONDITION]; -- used in the implementation of Restart MakeLP: PROC [size: Zone.BlockSize] RETURNS [lp: LONG POINTER] = BEGIN t: Environment.Base RELATIVE POINTER = MakeRP[size]; RETURN[@base[t]]; END; MakeRP: PROC [size: Zone.BlockSize] RETURNS [rp: Environment.Base RELATIVE POINTER] = BEGIN status: Zone.Status; [node: rp, s: status] ← ResidentHeap.MakeNode[size, a1]; IF status ~= okay THEN ErrorHalt[]; END; -- Drives FindDrive: PROC [drive: DiskChannel.Drive] RETURNS [driveH: DiskChannelBackend.DriveHandle] = INLINE { RETURN[LOOPHOLE[drive]]}; shortTime: CONDITION; AcquireDriveObject: ENTRY PROCEDURE [drive: DiskChannelBackend.DriveHandle] = BEGIN WHILE drive.locked DO WAIT driveObjectUnlocked; ENDLOOP; drive.locked ← TRUE; END; ReturnDriveObject: ENTRY PROCEDURE [drive: DiskChannelBackend.DriveHandle] = { drive.locked ← FALSE; BROADCAST driveObjectUnlocked; }; AwaitStateChange: PUBLIC PROC [ count: CARDINAL, type: Device.Type, index: CARDINAL] RETURNS [currentChangeCount: CARDINAL] = BEGIN DriveOK: PROC [drive: DiskChannelBackend.DriveHandle] RETURNS [BOOLEAN] = BEGIN IF type = Device.nullType THEN RETURN[index = drive.driveOrdinal OR index = PhysicalVolume.nullDeviceIndex] ELSE RETURN[ type = drive.driveID.type AND (index = drive.driveOrdinal OR index = PhysicalVolume.nullDeviceIndex)] END; GetCount: PROC RETURNS [count: CARDINAL] = BEGIN drive: DiskChannelBackend.DriveHandle ← g.firstDrive; nextdrive: DiskChannelBackend.DriveHandle; count ← 0; WHILE drive ~= NIL DO AcquireDriveObject[drive]; IF DriveOK[drive] THEN BEGIN -- we must poke drive to be sure that drive.changeCount is right [] ← drive.getStatus[drive]; count ← count + drive.changeCount; END; nextdrive ← drive.next; ReturnDriveObject[drive]; drive ← nextdrive; ENDLOOP END; WHILE (currentChangeCount ← GetCount[]) < count DO Wait[] ENDLOOP; END; Wait: ENTRY PROCEDURE = INLINE { WAIT shortTime }; GetDriveAttributes: PUBLIC PROC [drive: DiskChannel.Drive] RETURNS [ deviceType: Device.Type, deviceHandle: PilotDisk.Handle, deviceOrdinal: CARDINAL, nPages: DiskChannel.DiskPageCount, ready: BOOLEAN, state: DiskChannel.DriveState, changeCount: CARDINAL] = BEGIN driveID: DiskChannelBackend.DriveID; handle: DiskChannelBackend.DriveHandle ← FindDrive[drive]; AcquireDriveObject[handle]; ready ← handle.getStatus[handle].ready; [driveID: driveID, nPages: nPages, driveOrdinal: deviceOrdinal, state: state, changeCount: changeCount] ← handle↑; deviceType ← driveID.type; deviceHandle ← driveID.handle; ReturnDriveObject[handle]; END; GetDriveTag: PUBLIC PROC [drive: DiskChannel.Drive] RETURNS [CARDINAL] = BEGIN handle: DiskChannelBackend.DriveHandle ← FindDrive[drive]; tag: CARDINAL; AcquireDriveObject[handle]; tag ← handle.tag; ReturnDriveObject[handle]; RETURN[tag]; END; GetNextDrive: PUBLIC PROC [prev: DiskChannel.Drive] RETURNS [DiskChannel.Drive] = BEGIN GetFirstDrive: ENTRY PROC RETURNS[DiskChannel.Drive] = INLINE { RETURN[SealDrive[g.firstDrive]] }; handle: DiskChannelBackend.DriveHandle ← FindDrive[prev]; next: DiskChannel.Drive; IF prev = DiskChannel.nullDrive THEN next ← GetFirstDrive[] ELSE {AcquireDriveObject[handle]; next ← SealDrive[FindDrive[prev].next]; ReturnDriveObject[handle];}; RETURN[next]; END; GetPageNumber: PUBLIC PROC [ drive: DiskChannel.Drive, page: DiskChannel.Address] RETURNS [DiskChannel.DiskPageNumber] = BEGIN dH: DiskChannelBackend.DriveHandle = FindDrive[drive]; diskPageNumber: DiskChannel.DiskPageNumber; AcquireDriveObject[dH]; diskPageNumber ← dH.getPageNumber[dH, page]; ReturnDriveObject[dH]; RETURN[diskPageNumber]; END; SealDrive: PROC [driveID: DiskChannelBackend.DriveHandle] RETURNS [DiskChannel.Drive] = INLINE {RETURN[LOOPHOLE[driveID]]}; SetDriveState: PUBLIC PROC [ drive: DiskChannel.Drive, changeCount: CARDINAL, state: DiskChannel.DriveState] RETURNS [s: DiskChannel.DriveStatus] = BEGIN handle: DiskChannelBackend.DriveHandle = FindDrive[drive]; AcquireDriveObject[handle]; -- poke the drive so that drive.changeCount is current [] ← handle.getStatus[handle]; -- Special case changeCount of 0 for PhysicalVolumeImpl IF changeCount ~= 0 THEN IF changeCount ~= handle.changeCount THEN RETURN[invalidDrive]; IF NOT (handle.state = inactive OR state = inactive) THEN { ReturnDriveObject[handle]; RETURN[alreadyAsserted]; }; handle.changeState[handle, state]; handle.state ← state; ReturnDriveObject[handle]; RETURN[ok] END; SetDriveTag: PUBLIC PROC [drive: DiskChannel.Drive, tag: CARDINAL] = BEGIN handle: DiskChannelBackend.DriveHandle = FindDrive[drive]; AcquireDriveObject[handle]; handle.tag ← tag; ReturnDriveObject[handle]; END; -- Channels ChannelHandle: TYPE = Environment.Base RELATIVE POINTER TO ChannelObject; ChannelObject: TYPE = RECORD [ driveHandle: DiskChannelBackend.DriveHandle, completionHandle: CompletionHandle, -- used in the implementation of WaitAny changeCount: CARDINAL, suspendCount: [0..256), -- used in the implementation of Suspend, Restart ioCount: [0..256)]; -- used in the implementation of Idle Create: PUBLIC ENTRY PROC [ drive: DiskChannel.Drive, completion: DiskChannel.CompletionHandle] RETURNS [DiskChannel.Handle] = BEGIN OPEN comp: LOOPHOLE[completion, CompletionHandle]; chH: ChannelHandle; dH: DiskChannelBackend.DriveHandle = FindDrive[drive]; IF dH = NIL THEN RETURN[LOOPHOLE[DiskChannel.nullHandle]]; -- no such drive chH ← MakeRP[SIZE[ChannelObject]]; base[chH] ← ChannelObject[ driveHandle: dH, completionHandle: @comp, changeCount: dH.changeCount, suspendCount: 0, ioCount: 0]; RETURN[LOOPHOLE[chH]] END; Delete: PUBLIC ENTRY PROC [channel: DiskChannel.Handle] = { IF ResidentHeap.FreeNode[LOOPHOLE[channel]] # okay THEN ErrorHalt[]}; GetAttributes: PUBLIC ENTRY PROC [channel: DiskChannel.Handle] RETURNS [drive: DiskChannel.Drive] = {RETURN[SealDrive[GetDrive[channel]]]}; GetPageAddress: PUBLIC PROC [ channel: DiskChannel.Handle, page: DiskChannel.DiskPageNumber] RETURNS [DiskChannel.Address] = BEGIN OPEN channelObj: base[LOOPHOLE[channel, ChannelHandle]]; address: DiskChannel.Address; handle: DiskChannelBackend.DriveHandle = channelObj.driveHandle; AcquireDriveObject[handle]; address ← handle.getPageAddress[handle, page]; ReturnDriveObject[handle]; RETURN[address]; END; Idle: PUBLIC ENTRY PROC [channel: DiskChannel.Handle] = BEGIN OPEN channelObj: base[LOOPHOLE[channel, ChannelHandle]]; -- Should an error be returned if the change counts do not match? IF channelObj.changeCount ~= channelObj.driveHandle.changeCount THEN RETURN; channelObj.suspendCount ← channelObj.suspendCount + 1; WHILE channelObj.ioCount > 0 DO WAIT channelObj.completionHandle.ioComplete; ENDLOOP; END; InitiateIO: PUBLIC --EXTERNAL--PROC [req: DiskChannel.IORequestHandle] = BEGIN TouchChannel: ENTRY PROC [channelHandle: ChannelHandle] = -- do the monitored parts of setting up an IO request. -- INLINE BEGIN OPEN channelObj: base[channelHandle]; WHILE channelObj.suspendCount > 0 DO WAIT g.channelRestarted; ENDLOOP; channelObj.ioCount ← channelObj.ioCount + 1; END; CancelIO: ENTRY PROC [channelHandle: ChannelHandle] = INLINE BEGIN OPEN channelObj: base[channelHandle]; channelObj.ioCount ← channelObj.ioCount - 1; END; ch: ChannelHandle = LOOPHOLE[req.channel]; AcquireDriveObject[base[ch].driveHandle]; TouchChannel[ch]; IF base[ch].changeCount ~= base[ch].driveHandle.changeCount THEN BEGIN CancelIO[ch]; req.status ← invalidChannel; NotifyIOComplete[req]; RETURN; END; req.countDone ← 0; base[ch].driveHandle.requestIO[req]; ReturnDriveObject[base[ch].driveHandle]; END; Restart: PUBLIC ENTRY PROC [channel: DiskChannel.Handle] = BEGIN OPEN channelObj: base[LOOPHOLE[channel, ChannelHandle]]; IF (channelObj.suspendCount ← channelObj.suspendCount - 1) = 0 THEN BROADCAST g.channelRestarted; END; Suspend: PUBLIC ENTRY PROC [channel: DiskChannel.Handle] = BEGIN OPEN channelObj: base[LOOPHOLE[channel, ChannelHandle]]; channelObj.suspendCount ← channelObj.suspendCount + 1; END; WaitAny: PUBLIC ENTRY PROC [completion: DiskChannel.CompletionHandle] RETURNS [req: DiskChannel.IORequestHandle] = BEGIN OPEN LOOPHOLE[completion, CompletionHandle]; WHILE first = NIL DO WAIT ioComplete; ENDLOOP; req ← first; first ← req.next; END; -- Completions CompletionHandle: TYPE = LONG POINTER TO CompletionObject; CompletionObject: TYPE = RECORD [ first, last: DiskChannel.IORequestHandle, ioComplete: CONDITION]; ioCompletePrototype: CONDITION; -- do not ever wait on this CreateCompletionObject: PUBLIC PROC RETURNS [DiskChannel.CompletionHandle] = BEGIN Crock: TYPE = ARRAY [0..SIZE[CONDITION]) OF WORD; cH: CompletionHandle ← MakeLP[SIZE[CompletionObject]]; cH.first ← NIL; -- cH.ioComplete ← ioCompletePrototype; LOOPHOLE[@cH.ioComplete, LONG POINTER TO Crock]↑ ← LOOPHOLE[ioCompletePrototype]; RETURN[LOOPHOLE[cH]] END; -- Backend GetDrive: PUBLIC PROC [channel: DiskChannel.Handle] RETURNS [DiskChannelBackend.DriveHandle] = -- INLINE BEGIN OPEN channelObj: base[LOOPHOLE[channel, ChannelHandle]]; RETURN[channelObj.driveHandle] END; NotifyIOComplete: PUBLIC ENTRY PROC [req: DiskChannel.IORequestHandle] = -- used by driver to indicate some IO transaction has completed. BEGIN OPEN channelObj: base[LOOPHOLE[req.channel, ChannelHandle]], channelObj.completionHandle; channelObj.ioCount ← channelObj.ioCount - 1; req.next ← NIL; IF first = NIL THEN first ← req ELSE last.next ← req; last ← req; BROADCAST ioComplete; END; RegisterDrive: PUBLIC ENTRY PROC [drive: DiskChannelBackend.DriveHandle] = -- Add new drive to end of list, to preserve enumeration order defined by faces BEGIN prevDrive: DiskChannelBackend.DriveHandle; drive.driveOrdinal ← 0; -- in case first of type drive.next ← NIL; drive.locked ← FALSE; IF g.firstDrive = NIL THEN g.firstDrive ← drive ELSE BEGIN FOR prevDrive ← g.firstDrive, prevDrive.next WHILE prevDrive.next ~= NIL DO ENDLOOP; prevDrive.next ← drive; IF prevDrive.driveID.type = drive.driveID.type THEN drive.driveOrdinal ← prevDrive.driveOrdinal + 1; END; END; -- Initialization g.firstDrive ← NIL; Process.SetTimeout[@ioCompletePrototype, Process.MsecToTicks[1000]]; Process.InitializeCondition[@g.channelRestarted, Process.MsecToTicks[1000]]; Process.InitializeCondition[@shortTime, Process.MsecToTicks[5000]]; Process.InitializeCondition[@driveObjectUnlocked, Process.MsecToTicks[30000]]; END. LOG Time: January 11, 1979 3:28 PM By: Horsley Action: Create file Time: August 14, 1979 11:25 PM By: Redell Action: New DiskChannel Time: August 16, 1979 11:04 PM By: Redell Action: Add drive tags Time: October 19, 1979 9:11 AM By: McJones Action: Avoid calling InitializeCondition in CreateCompletionObject Time: November 29, 1979 3:21 PM By: Gobbel Action: New DiskChannel interface: initialize countDone field of IORequest Time: January 31, 1980 4:53 PM By: McJones Action: New DiskChannel; preserve order of drive registration Time: June 11, 1980 3:24 PM By: Luniewski Action: Changes for DiskChannel.Drive's being LONG POINTER's to DriveObject's. Implement AwaitStateChange, SetDriveState. Make changes to the channel implementation to account for removable volumes. Time: June 23, 1980 4:16 PM By: McJones Action: OISDisk=>PilotDisk Time: July 19, 1980 3:09 PM By: Jose Action: Changes to SetDriveState to fix deadlock, handle change of state. Time: July 30, 1980 2:15 PM By: Luniewski Action: Modify SetDriveState to return status. Time: August 7, 1980 9:27 AM By: Luniewski Action: Add support for stateChangeLock in driveObject. Time: September 16, 1980 11:59 PM By: Jose Action: Get ready before changeCount in GetDriveAttributes, change stateChangeLock to stateChangeLocked. Time: October 3, 1980 12:07 PM By: Luniewski Action: AwaitStateChange: access all drives Time: December 9, 1980 7:49 PM By: Yokota Action: Several ENTRY procs are converted to procs and Monitor lock is done by "locked" of each DriveObject.