DIRECTORY Basics USING [ LongDivMod ], Disk USING [PageCount, PageNumber, Request, Status], DiskFace USING [ControllerHandle, DeterminationMode, DetermineDiskShape, DeviceHandle, DiskAddress, DontCare, GetControllerAttributes, GetDeviceAttributes, GetDeviceType, GetNextController, GetNextDevice, InitializeCleanup, InitializeController, Initiate, Label, nullControllerHandle, nullDeviceHandle, Operation, OperationPtr, operationSize, Poll, Status, Type], PrincOpsUtils USING[ AllocateNakedCondition ], Process USING[ Detach, SecondsToTicks, SetPriority, SetTimeout, priorityFaultHandlers ], ProcessorFace USING[ GetClockPulses ], VM USING[ lowCore ]; DiskImpl: MONITOR IMPORTS Basics, DiskFace, PrincOpsUtils, Process, ProcessorFace, VM EXPORTS Disk, DiskFace = BEGIN Channel: TYPE = LONG POINTER TO ChannelObject; ChannelObject: PUBLIC TYPE = MONITORED RECORD[ drive: DiskFace.DeviceHandle, ordinal: CARDINAL, -- above fields are immutable; remainder are protected by monitor lock -- cylinders, movingHeads, fixedHeads, sectorsPerTrack: CARDINAL _ 0, freeOps: LONG POINTER TO Operation, freed: CONDITION, taken: CONDITION, waiters: INT, rest: Channel]; OperationPtr: TYPE = LONG POINTER TO Operation; Operation: TYPE = MACHINE DEPENDENT RECORD[ rest(0): OperationPtr, wait(2): CONDITION, status(4): DiskFace.Status, label(5): LONG POINTER TO DiskFace.Label, fill(7): ARRAY [7..opFieldPosition) OF WORD, faceOp(opFieldPosition): DiskFace.Operation, extra(opFieldPosition+SIZE[DiskFace.Operation]): SEQUENCE COMPUTED CARDINAL OF WORD ]; opFieldPosition: CARDINAL = 16; -- N.B. This must be a multiple of 16 to align DiskFace.Operation channelList: Channel _ NIL; NextChannel: PUBLIC SAFE PROC[prev: Channel, wait: BOOL] RETURNS[next: Channel] = TRUSTED BEGIN DO next _ EntryNext[prev]; IF next # NIL OR NOT wait THEN EXIT; [] _ AwaitChange[AwaitChange[0]]; ENDLOOP; END; change: CONDITION; AwaitChange: ENTRY PROC[old: INT] RETURNS[new: INT] = TRUSTED { ENABLE UNWIND => NULL; DO WAIT change ENDLOOP--TEMP!!-- }; lastDrive: DiskFace.DeviceHandle _ DiskFace.nullDeviceHandle; EntryNext: ENTRY PROC[prev: Channel] RETURNS[next: Channel] = TRUSTED BEGIN ENABLE UNWIND => NULL; next _ IF prev = NIL THEN channelList ELSE prev.rest; -- check whether next drive has changed state -- IF next = NIL THEN BEGIN -- check whether there is a new drive -- new: DiskFace.DeviceHandle = DiskFace.GetNextDevice[lastDrive]; IF new # DiskFace.nullDeviceHandle THEN { Assign[prev, NewDrive[new]]; lastDrive _ new }; next _ IF prev = NIL THEN channelList ELSE prev.rest; END; END; Assign: INTERNAL PROC[tail: Channel, new: Channel] = TRUSTED{ IF tail = NIL THEN channelList _ new ELSE tail.rest _ new }; lastOrdinal: CARDINAL _ 0; opsPerDrive: INT _ 2; NewDrive: INTERNAL PROC[new: DiskFace.DeviceHandle] RETURNS[channel: Channel] = TRUSTED BEGIN channel _ VM.lowCore.NEW[ChannelObject _ [ drive: new, ordinal: lastOrdinal, freeOps: NIL, waiters: 0, rest: NIL] ]; lastOrdinal _ lastOrdinal+1; FOR i: INT IN [0..opsPerDrive) DO new: OperationPtr = VM.lowCore.NEW[Operation[DiskFace.operationSize-SIZE[DiskFace.Operation]]]; new.label _ VM.lowCore.NEW[DiskFace.Label]; new.rest _ channel.freeOps; channel.freeOps _ new; ENDLOOP; END; InspectDiskShape: PUBLIC SAFE PROC[channel: Channel, mode: DiskFace.DeterminationMode] RETURNS[nowKnown: BOOL] = TRUSTED BEGIN myOp: OperationPtr = AllocOp[channel]; nowKnown _ DiskFace.DetermineDiskShape[channel.drive, @myOp.faceOp, mode ! UNWIND => FreeOp[channel, myOp];]; FreeOp[channel, myOp]; IF nowKnown THEN BEGIN cylinders, movingHeads, fixedHeads, sectorsPerTrack: CARDINAL; [cylinders, movingHeads, fixedHeads, sectorsPerTrack] _ DiskFace.GetDeviceAttributes[channel.drive]; NoteShapeKnown[channel, cylinders, movingHeads, fixedHeads, sectorsPerTrack]; END; END; NoteShapeKnown: ENTRY PROC[channel: Channel, cylinders, movingHeads, fixedHeads, sectorsPerTrack: CARDINAL] = BEGIN ENABLE UNWIND => NULL; channel.cylinders _ cylinders; channel.movingHeads _ movingHeads; channel.fixedHeads _ fixedHeads; channel.sectorsPerTrack _ sectorsPerTrack; END; SameDrive: PUBLIC SAFE PROC[a,b: Channel] RETURNS[BOOL] = TRUSTED { RETURN[ a.ordinal = b.ordinal ] }; Valid: PUBLIC SAFE PROC[channel: Channel] RETURNS[BOOL] = TRUSTED { RETURN[TRUE] }; DriveAttributes: PUBLIC ENTRY SAFE PROC[channel: Channel] RETURNS[ type: DiskFace.Type, ordinal: INT, ready: BOOL, nPages: Disk.PageCount] = TRUSTED BEGIN ENABLE UNWIND => NULL; type _ DiskFace.GetDeviceType[channel.drive]; ordinal _ channel.ordinal; nPages _ (LONG[channel.cylinders] * channel.movingHeads) * channel.sectorsPerTrack; END; -- Performing operations! -- GetAddress: ENTRY PROC[channel: Channel, page: INT] RETURNS[addr: DiskFace.DiskAddress] = BEGIN ENABLE UNWIND => NULL; cylinder: CARDINAL; temp: CARDINAL; [quotient: cylinder, remainder: temp] _ Basics.LongDivMod[ num: page, den: channel.sectorsPerTrack * channel.movingHeads]; RETURN[ [cylinder: cylinder, sector: temp MOD channel.sectorsPerTrack, head: temp/channel.sectorsPerTrack ] ] END; AllocOp: ENTRY PROC[channel: Channel] RETURNS[op: OperationPtr] = BEGIN ENABLE UNWIND => NULL; IF channel.freeOps = NIL THEN BEGIN channel.waiters _ channel.waiters+1; WHILE channel.freeOps = NIL DO WAIT channel.freed[ ! UNWIND => channel.waiters _ channel.waiters-1 ] ENDLOOP; channel.waiters _ channel.waiters-1; END; op _ channel.freeOps; channel.freeOps _ op.rest; op.rest _ NIL; BROADCAST channel.taken; END; FreeOp: ENTRY PROC[channel: Channel, op: OperationPtr] = BEGIN ENABLE UNWIND => NULL; op.rest _ channel.freeOps; channel.freeOps _ op; IF channel.waiters > 0 THEN BEGIN -- let any waiter have the processor, to enqueue the next request NOTIFY channel.freed; IF channel.waiters ~= 0 THEN WAIT channel.taken; END; END; largestRequestRun: INT = MIN[LAST[CARDINAL], 1000]; DoIO: PUBLIC UNSAFE PROC[channel: Channel, label: LONG POINTER TO DiskFace.Label, request: LONG POINTER TO Disk.Request] RETURNS[ status: Disk.Status, countDone: Disk.PageCount] = TRUSTED BEGIN myOp: OperationPtr = AllocOp[channel]; countDone _ 0; status _ [unchanged[goodCompletion]]; -- in case request.count is already zero UNTIL request.count = 0 DO BEGIN -- Loop to break up large transfers. ENABLE UNWIND => FreeOp[channel, myOp]; reqPages: INT = MIN[request.count, largestRequestRun]; doneThisTime: INT; myOp.label^ _ label^; myOp.faceOp _ [ clientHeader: GetAddress[channel, request.diskPage], labelPtr: myOp.label, dataPtr: request.data, incrementDataPtr: request.incrementDataPtr, unused: NULL, command: request.command, tries: request.tries, pageCount: reqPages, deviceStatus: NULL, diskHeader: NULL, device: channel.drive ]; myOp.status _ inProgress; DoOperation[myOp]; doneThisTime _ reqPages - myOp.faceOp.pageCount; countDone _ countDone + doneThisTime; status _ [unchanged[myOp.status]]; request.diskPage _ [request.diskPage + doneThisTime]; label^ _ myOp.label^; request.data _ myOp.faceOp.dataPtr; request.count _ request.count - doneThisTime; IF myOp.status # goodCompletion THEN EXIT; END; ENDLOOP; FreeOp[channel, myOp]; END; currentOperations: CARDINAL _ 0; Pulses: TYPE = LONG CARDINAL; becameActive: Pulses; -- pulse reading when we changed from inactive to active activeTotal: Pulses _ 0; -- total number of pulses while we have been active totalReads, totalWrites, totalReadPages, totalWritePages: INT _ 0; GetStatistics: PUBLIC ENTRY SAFE PROC RETURNS[ active, total: Pulses, reads, writes, readPages, writePages: INT ] = CHECKED BEGIN ENABLE UNWIND => NULL; total _ ProcessorFace.GetClockPulses[]; active _ activeTotal + (IF currentOperations#0 THEN (total - becameActive) ELSE 0); reads _ totalReads; writes _ totalWrites; readPages _ totalReadPages; writePages _ totalWritePages; END; DoOperation: ENTRY PROC[myOp: OperationPtr] = BEGIN ENABLE UNWIND => NULL; IF myOp.faceOp.command.data = write OR myOp.faceOp.command.label = write THEN {totalWrites _ totalWrites+1; totalWritePages _ totalWritePages+myOp.faceOp.pageCount } ELSE {totalReads _ totalReads+1; totalReadPages _ totalReadPages+myOp.faceOp.pageCount }; DiskFace.Initiate[@myOp.faceOp]; IF currentOperations = 0 THEN becameActive _ ProcessorFace.GetClockPulses[]; currentOperations _ currentOperations+1; WHILE myOp.status = inProgress DO WAIT myOp.wait ENDLOOP; END; DiskInterrupt: ENTRY PROC[controller: DiskFace.ControllerHandle] = BEGIN cv: LONG POINTER TO CONDITION; mask: WORD; size: CARDINAL = DiskFace.GetControllerAttributes[controller]; GlobalState: TYPE = RECORD[SEQUENCE COMPUTED CARDINAL OF WORD]; globalState: LONG POINTER = VM.lowCore.NEW[GlobalState[size]]; [cv, mask] _ PrincOpsUtils.AllocateNakedCondition[]; -- Enable a timeout because disk world-swap can give our interrupt to the debugger! Process.SetTimeout[cv, Process.SecondsToTicks[1]]; DiskFace.InitializeController[controller, globalState, mask]; DiskFace.InitializeCleanup[controller]; Process.SetPriority[Process.priorityFaultHandlers]; DO -- forever, once per naked wakeup status: DiskFace.Status; faceOp: DiskFace.OperationPtr; retriedCount: CARDINAL; myOp: OperationPtr; WAIT cv; -- wakeup after one or more requests have completed DO -- take all completed requests from controller [status, faceOp, retriedCount] _ DiskFace.Poll[controller]; IF status = inProgress OR faceOp = NIL THEN EXIT; myOp _ LOOPHOLE[faceOp-opFieldPosition]; myOp.status _ status; currentOperations _ currentOperations-1; IF currentOperations = 0 THEN activeTotal _ activeTotal + (ProcessorFace.GetClockPulses[] - becameActive); NOTIFY myOp.wait; ENDLOOP; ENDLOOP; END; InitializeControllers: ENTRY PROC = BEGIN FOR this: DiskFace.ControllerHandle _ DiskFace.GetNextController[DiskFace.nullControllerHandle], DiskFace.GetNextController[this] UNTIL this = DiskFace.nullControllerHandle DO Process.Detach[FORK DiskInterrupt[this]] ENDLOOP; END; --DiskFace.--DontCare: PUBLIC TYPE = DiskFace.DiskAddress; GetBootChainLink: PUBLIC SAFE PROC[channel: Channel, diskPage: Disk.PageNumber] RETURNS [DiskFace.DontCare] = TRUSTED { RETURN[ GetAddress[channel, diskPage] ] }; GetPageNumber: PUBLIC ENTRY SAFE PROC[channel: Channel, link: DontCare] RETURNS[ Disk.PageNumber] = TRUSTED { RETURN[ [LONG[link.cylinder * channel.movingHeads + link.head] * channel.sectorsPerTrack + link.sector] ] }; GetDeviceFromChannel: PUBLIC ENTRY SAFE PROC[channel: Channel] RETURNS [DiskFace.DeviceHandle] = TRUSTED { RETURN [channel.drive];}; InitializeControllers[]; END. ΄DiskImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Andrew Birrell October 28, 1983 10:11 am Levin, August 8, 1983 3:30 pm Bob Hagmann, May 10, 1984 8:47:57 am PDT Russ Atkinson (RRA) January 30, 1985 10:47:05 pm PST WHILE next # NIL AND next.changeCount # DiskChannel.GetDriveAttributes[next.oldDrive].changeCount DO Assign[prev, next.rest]; next _ next.rest; ENDLOOP; The largest number of pages in a single request handed to the DiskFace. DiskFace.Operation limits it to a CARDINAL, and we restrict it further to avoid hogging of the device by a single client. Beware: if you ask DiskFace to transfer 0 pages, it may transfer 64K pages instead (so don't)! Κ Ζ˜codešœ ™ Kšœ Οmœ1™šœ7˜7Kšœ,˜,—KšœM˜MKšžœ˜—Kšžœ˜K˜—š œžœžœHžœ˜mKšž˜Kšžœžœžœ˜Kšœ˜Kšœ"˜"Kšœ ˜ Kšœ*˜*Kšžœ˜—K˜š   œžœžœžœžœžœ˜9Kšžœžœ˜,K˜—š  œžœžœžœžœžœ˜9šžœžœžœ˜K˜——š œžœžœžœžœžœ žœ žœž˜”Kšž˜Kšžœžœžœ˜K˜-K˜Kšœ žœE˜SKšžœ˜—K˜K˜K˜KšŸ˜K˜š   œžœžœžœžœ˜YKšž˜Kšžœžœžœ˜Kšœ žœ˜Kšœžœ˜šœ:˜:Kšœ?˜?—šžœ˜Kšœ žœ@˜P—Kšžœ˜—K˜š œžœžœžœ˜AKšž˜Kšžœžœžœ˜Kšžœž˜šžœž˜ K˜$Kšžœž˜Kšžœžœžœ*žœ˜QKšœ$˜$Kšžœ˜—Kšœ;žœ˜?Kšž œ˜Kšžœ˜—K˜š œžœžœ&˜8Kšž˜Kšžœžœžœ˜Kšœ0˜0Kšžœ˜šžœžœŸA˜LKšžœ˜Kšžœžœžœ˜0Kšžœ˜—Kšžœ˜—K˜š œžœžœžœžœ ˜3KšœΒ™Β—K˜š œžœžœžœžœžœžœžœžœžœžœ4ž˜»Kšž˜Kšœ&˜&Kšœ˜Kšœ&Ÿ(˜NKšœ_™_Kšžœ˜šžœžœŸ$˜-Kšžœžœ˜'Kšœ žœžœ#˜6Kšœžœ˜Kšœ˜˜Kšœ4˜4Kšœ˜Kšœ˜Kšœ+˜+Kšœžœ˜ K˜Kšœ˜Kšœ˜Kšœžœ˜Kšœ žœ˜K˜K˜—K˜K˜Kšœ0˜0Kšœ%˜%K˜"Kšœ5˜5Kšœ˜Kšœ#˜#Kšœ-˜-Kšžœžœžœ˜*Kšžœ˜—Kšžœ˜K˜Kšžœ˜—K˜Kšœžœ˜ Kšœžœžœžœ˜KšœŸ8˜NKšœŸ3˜LKšœ:žœ˜BK˜š   œžœžœžœžœžœ˜.Kšœ=žœž˜LKšž˜Kšžœžœžœ˜Kšœ'˜'Kšœžœžœžœ˜SK˜K˜K˜K˜Kšžœ˜—K˜š  œžœžœ˜-Kšž˜Kšžœžœžœ˜Kšžœ"žœ"˜HKšžœX˜\KšžœU˜YKšœ ˜ Kšžœžœ/˜LK˜(Kšžœžœžœ žœ˜9Kšžœ˜K˜—š  œžœžœ)˜BKšž˜Kš œžœžœžœž œ˜Kšœžœ˜ Kšœžœ0˜>Kšœ žœžœžœžœžœžœžœ˜?Kšœ žœžœžœ˜>Kšœ4˜4K˜SK˜2Kšœ=˜=K˜'K˜3šžœŸ!˜$K˜K˜Kšœžœ˜K˜KšžœŸ3˜<šžœŸ/˜2K˜;Kš žœžœ žœžœžœ˜1Kšœžœ˜(K˜K˜(Kšžœ˜KšžœM˜QKšžœ ˜—Kšžœ˜—Kšžœ˜Kšžœ˜—K˜š œžœžœ˜#Kšž˜šžœ]˜`Kšœ ˜ —Kšžœ%˜*Kšžœžœžœ˜4Kšžœ˜—K˜KšŸ œ žœžœ˜:K˜š  œžœžœžœ.žœž˜uKšœžœ$˜,—K˜š   œžœžœžœžœ#žœž˜kšœžœžœK˜ZKšœ˜——K˜Kš œžœžœžœžœžœžœžœ˜…K˜K˜K˜Kšžœ˜K˜K˜—…—(Μ8F