-- 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.