.Title VFDRV - VF: Virtual Disk Driver .Enabl Lc .Ident /V03.00/ ; ; ; VF: (Virtual Disk) device driver. ; ; Version: V03.00 ; Operating Systems: RSX-11M-PLUS V3.0 ; MICRO/RSX V3.0 ; (has NOT been tested under P/OS) ; ; Originally written by Ralph Stamerjohn ; ; Previously modified by: ; ; R.S. Mearns ; G. Everhart ; ; Updated by: G. L. Maxwell ; U.S. Geological Survey ; 27-AUG-86 ; ; ************************************************************************ ; * ; Although this program has been tested by the Geological Survey, * ; United States Department of the Interior, no warranty, expressed or * ; implied, is made by the Geological Survey as to the accuracy and * ; functioning of the program and related program material nor shall * ; the fact of distribution constitute any such warranty, and no respon- * ; sibility is assumed by the Geological Survey in connection therewith. * ; * ; Full permission and consent is hereby given to DECUS and to the DECUS * ; Special Interest Groups to reproduce, distribute, and publish and * ; permit others to reproduce in whole or in part, in any form and * ; without restriction, this program and any information relating thereto * ; * ; ************************************************************************ ; ; This driver and its associated tasks (AVF, DVF) provide a mechanism ; for creating a logical disk drive out of one or more contiguous ; container files located on Files-11 mounted volumes (which may themselves ; be virtual disks). ; ; When an I/O request is received, the Virtual Disk driver translates the ; logical block number requested for the Virtual Disk to the logical ; block number on the appropriate target volume. The I/O packet is ; then queued to the target volume's driver. ; ; The Internal I/O Completion mechanism in RSX-11M-PLUS is utilized by ; this driver. (This differs from the previous implementations of the ; Virtual Disk driver.) When the target volume's driver completes the I/O ; operation, $IOFIN returns to this driver with the I/O packet to ; perform additional processing. This is done so that Virtual Disks may ; be shadowed. A disadvantage is that the Virtual Disk UCB and SCB remain ; marked as busy until the I/O completes. Therefore, one UCB and one SCB are ; built for each Virtual Disk unit to maximize parallelism. ; ; ; Macro Calls ; .MCALL PKTDF$,SHDDF$,DCBDF$ PKTDF$ ; Define I/O packet offsets SHDDF$ ; Define shadow recording contol offsets DCBDF$ ; Define DCB offsets ; ; Local Data: ; .IF DF P$$LOG .MCALL TCBDF$ TCBDF$ SERNAM: .RAD50 /VFL.../ SERVER: .WORD 0 ; TCB address of server task .ENDC ; DF P$$LOG ; ; Device Dispatch Table ; DDT$ VF,0,NONE,,,NEW ;+ ; **-VFINI - Virtual Disk I/O Initiation Entry Point ; ; This routine is entered from the Queue I/O Directive when an I/O ; request is generated by a task. The driver is called at this point ; before the I/O packet is placed in the device queue. The driver ; performs checks on the packet before allowing it to be queued. ; ; In particular, we reject all requests for the prototype unit, VF0:. ; This is done specifically because the driver marks VF0: as "busy" ; so that the driver may not be unloaded while virtual units with other ; unique names have context. ; ; Inputs: ; R5= Address of the UCB of the controller to be initiated. ; R4= Address of the SCB of the controller to be initiated. ; R1= Address of the I/O packet. ; ;- VFINI:: MOV U.DCB(R5),R3 ; Get address of the device DCB CMP #<"VF>,D.NAM(R3) ; Is the device name "VF"? BNE 10$ ; If ne no, accept the packet TST D.UNIT(R3) ; Is this VF0:? BNE 10$ ; If ne no, accept the packet MOV R1,R3 ; Copy the I/O packet address MOV #IE.PRI&377,R0 ; Return privilege violation CLR R1 ; Clear bytes transfered count CALLR $IOFIN ; Finish the I/O and return 10$: MOV R4,R0 ; Copy pointer to I/O queue listhead ; ; Inputs to $QINSP: ; ; R0 = Address of two word listhead ; R1 = Address of packet to be entered in queue ; ; Outputs from $QINSP: None. ; CALL $QINSP ; Insert packet in I/O queue ; ; Begin serial processing of I/O packets. ; ; This marks where the driver initiates a new function in order to ; propagate the execution of the driver. If the specified "controller" ; is not busy, then an attempt is made to dequeue the next I/O request. ; Else a return to the caller is executed. If the dequeue attempt ; is successful, then the next I/O operation is initiated. Control ; returns to the driver following completion of each virtual I/O request. ; ; Inputs: ; ; R5 = Address of the UCB of the controller. ; ; Outputs: ; ; If the specified controller is not busy and an I/O request is ; waiting to be processed, then the request is dequeued and the ; driver initiates the requested I/O function VFINI1:: CALL $GTPKT ; Get next I/O packet to process BCC 10$ ; Proceed if there is work to do RETURN ; Exit driver if no more work 10$: ; Reference label ; ; The following arguments are returned by $GTPKT: ; ; R1= Adrs of the I/O request packet ; R2= Physical unit number of the requested drive ; R3= Controller index ; R4= Adrs of the Status Control Block (SCB) ; R5= Adrs of the UCB of the drive to be initiated ; ; Virtual Disk Driver I/O request packet format: ; ; WD. 00 -- I/O queue thread word ; WD. 01 -- Request priority, event flag number ; WD. 02 -- Adrs of the TCB of the requestor task ; WD. 03 -- Pointer to 2nd Lun word in requestor task header ; WD. 04 -- Contents of first Lun word (UCB adrs) ; WD. 05 -- I/O function code ; WD. 06 -- Virtual adrs of I/O status block ; WD. 07 -- Relocation bias of I/O status block ; WD. 10 -- I/O status block address (displacement +140000) ; WD. 11 -- Virtual adrs of AST service routine ; WD. 12 -- Memory extension bits of I/O transfer ; WD. 13 -- Buffer adrs of I/O transfer ; WD. 14 -- Number of bytes to be transfered ; WD. 15 -- Not used. ; WD. 16 -- Low byte contains high part of logical block number ; WD. 17 -- Low part of logical block number of I/O request ; WD. 20 -- Not used ; WD. 21 -- Not used ; ; Driver usage of words in I/O packet: ; ; I.IOSB is used by the driver to store the VF: UCB address ; so that it may be retrieved following the completion ; of the internal I/O request ; I.IOSB+2 is used to hold the APR 5 bias of this driver ; I.IOSB+4 holds the internal I/O completion routine within this ; driver, Or'ed with bit zero. ; ; I.PRM Parameter word offsets are manipulated as required ; when VFDRV passes a request to another driver. ; ; ; ** NOTE THE FOLLOWING ** ; ; There is a possibility at this point that the UCB offsets U.BUF, ; U.BUF+2, and U.CNT, in addition to S.STS and U.STS, have not been ; properly set up, as though we called $GSPKT instead of $GTPKT. ; ; This happens (only?) when this virtual device is the primary unit of ; a shadowed pair, and the secondary unit's container file is located ; on a device whose driver uses $GSPKT (e.g., DUDRV), and the I/O ; function is IO.WLB. The following scenario occurs: ; ; 1. $GTPKT is called to obtain the primary packet ; 2. Since the function is a write, $GTPKT queues the secondary ; packet to the secondary device (another virtual disk); ; if the secondary device is idle, it is called. ; 3. $GTPKT is called again to retrieve the secondary packet. ; 4. The secondary packet is processed and passed to the ; target driver; if it is idle, it is called. ; 5. The target driver calls $GSPKT to obtain the packet and ; starts processing. The low core location $TEMP2 is loaded ; with the address of the driver's acceptance routine. ; 6. Return is eventually made to the $GTPKT context for the ; primary packet. ; 7. Since $TEMP2 now holds the non-standard acceptance routine ; vector, the primary unit's data structures are not ; initialized, and the unit is not marked busy. ; ; Hence, the following code is inserted here.... ; TSTB S.STS(R4) ; Have we been marked busy? BNE 20$ ; If ne yes, data structures are OK MOV I.PRM(R1),U.BUF(R5) ; Set up UCB structures for later MOV I.PRM+2(R1),U.BUF+2(R5) MOV I.PRM+4(R1),U.CNT(R5) INCB S.STS(R4) ; Mark SCB as busy BISB #US.BSY,U.STS(R5) ; Mark UCB as busy 20$: ; Reference label .IF DF P$$LOG ; Copy the packet into the area in the UCB MOV R5,R0 ; Copy the UCB address ADD #U.PKT,R0 ; Point to the area to store it MOV R1,R2 ; Copy the I/O packet address MOV #I.LGTH/2,R3 ; Length of packet 29000$: MOV (R2)+,(R0)+ ; Copy the packet SOB R3,29000$ ; Until done .ENDC ; DF P$$LOG ; ; *** NOTE *** ; ; Disk Data Caching currently does not work (the system crashes), although ; the author does not know why. ; ; If some brave soul figures it out, be sure to enable the following code, ; and enable IO.STC as a valid control function in the driver dispatch table. ; ; If this is a control function, always return success. IO.STC is ; a case we want to handle in the driver (instead of making it a no-op) ; so that virtual disks can be cached. ; ; ; CMPB #IO.STC/256.,I.FCN+1(R1) ; Control function? ; BNE 30$ ; If ne no ; MOV #IS.SUC&377,R0 ; Get final status ; JMP VFALT ; And finish I/O ;30$: ; Reference label ; ; ** Block Check the I/O request ** ; ; Inputs to $BLKCK: ; ; R1 Contains the I/O packet address ; R5 Contains the UCB address ; ; Outputs from $BLKCK: ; ; R0 Contains the low order 16 bits of the starting LBN ; R1 Points to I.PRM+12 (low order LBN value) ; R2 Contains the high order bits of the starting LBN ; R3 Contains the I/O Packet address ; CALL $BLKCK ; Perform Block check (no return on error) ; ; Check function code for transfer function. All others should have ; been no-op'ed by now. ; CMPB #IO.RLB/256.,I.FCN+1(R3) ; Read logical? BLO 100$ ; If lo then invalid function BEQ 200$ ; Go process read request CMPB #IO.WLB/256.,I.FCN+1(R3) ; Write logical? BEQ 190$ ; If eq do write protect check 100$: MOV #IE.IFC&377,R0 ; Get invalid function code JMP VFALT ; And terminate I/O ; ; Check if this unit was created as a read-only device ; (akin to hardware write protect) ; 190$: BITB #XF.WPR,U.XFLG(R5) ; Write-protected device? BEQ 200$ ; If eq no -- accept request MOV #IE.WLK&377,R0 ; Get write-lock error JMP VFALT ; Terminate I/O ; ; At this point, we are ready to initiate the I/O. ; ; First, perform preliminary manipulations on the I/O packet ; and initialize our context variables ; 200$: MOV I.IOSB(R3),U.IOSB(R5) ; Save I/O status virtual address MOV I.IOSB+2(R3),U.IOSB+2(R5) ; Save I/O status bias MOV I.IOSB+4(R3),U.IOSB+4(R5) ; Save I/O status displacement MOV R5,I.IOSB(R3) ; Save our UCB address in the packet MOV KISAR5,I.IOSB+2(R3) ; Save our own bias for I/O completion MOV #,I.IOSB+4(R3) ; Save I/O completion entry point MOV R2,U.XLBN(R5) ; Save high order starting LBN MOV R0,U.XLBN+2(R5) ; Save low order starting LBN CLR U.ISB2(R5) ; Clear working I/O status word 2 cell MOV #140000,R4 ; Initialize secondary block displacement ; ; We like to work with buffer addresses as 22-bit quantities. Since we have ; set UC.NPR in our UCB, we get a 22 bit buffer address in U.BUF and U.BUF+2 ; (with the high order 6 bits in the upper byte of U.BUF). ; However, if we are running on an 18-bit machine, $MPPHY shoves the high ; order 2 bits into bits 4 and 5 of U.BUF. Hence, the following conditional: ; .IF NDF M$$EXT MOV U.BUF(R5),R1 ; Get high order bits of buffer address ASH #4,R1 ; Shift them back to the high byte MOV R1,U.BUF(R5) ; And store them back .ENDC ; NDF M$$EXT ; ; Verify that the secondary block bias is non-zero. This should ; only happen for a deassigned device which somehow was not marked ; offline. I don't see how that could happen, but if I don't check ; for it, it probably would happen.... ; MOV U.CTLP(R5),R1 ; Get the secondary block bias BNE 290$ ; If ne it exists MOV #IE.DNR&377,R0 ; Return device not ready JMP VFALT ; Finish the I/O 290$: MOV R1,KISAR6 ; Map secondary control block ; ; Now we look for the file which we will use for the first part of the ; transfer. At this point: ; ; R0 contains low order starting LBN ; R2 contains high order LBN ; R4 contains the secondary block displacement. ; 300$: SUB X.SIZE+2(R4),R0 ; Subtract size of next file SBC R2 ; Propagate a borrow SUB X.SIZE(R4),R2 ; Subtract high order BLT VFNEXT ; If LT then we start with this file ADD #X.LEN,R4 ; Point to the next file block BR 300$ ; And try again ; ; ** Top of Dispatch Loop ** ; ; Label VFNEXT marks the start of processing to dispatch the virtual request ; to the next driver. This code calculates the starting LBN and size of ; the request to pass on to the driver, and updates the buffer address ; and total size of transfer as required. ; ; Register usage during this part of the code: ; ; R5 Virtual device UCB address ; R4 Secondary block address (points to current file) ; R3 I/O Packet address ; R0-R2 Scratch variables ; ; First, calculate offset into this file where we begin the transfer ; VFNEXT: CMPB #IO.WLB/256.,I.FCN+1(R3) ; Write logical? BNE 400$ ; If ne no -- bypass read-only check BIT #XF.RON,X.FLAG(R4) ; Read-only container file? BEQ 400$ ; If eq no -- accept request MOV #IE.WLK&377,R0 ; Get write-lock error JMP VFDON ; Terminate I/O 400$: MOV U.XLBN(R5),R0 ; Get high order starting LBN MOV U.XLBN+2(R5),R1 ; Get low order LBN SUB X.OFF+2(R4),R1 ; Subtract VF: base offset on this file SBC R0 SUB X.OFF(R4),R0 ; R0,R1 hold LBN offset into this file MOV R0,I.PRM+10(R3) ; Store offset in I/O packet MOV R1,I.PRM+12(R3) ; ADD X.BASE+2(R4),I.PRM+12(R3) ; Add physical base LBN of file ADC I.PRM+10(R3) ADD X.BASE(R4),I.PRM+10(R3) ; Physical LBN of target device ; ; Now calculate how much of our remaining I/O request can be satisfied ; MOV X.SIZE+2(R4),R2 ; Get low order file size SUB R1,R2 ; Subtract starting LBN MOV X.SIZE(R4),R1 ; Get high order size (preserve carry) SBC R1 ; Propagate a borrow SUB R0,R1 ; R1,R2 contain remaining blocks in file ; ; *** BUGCHECK -- Remaining blocks had better be greater than zero! ; .IF DF D$$BUG!X$$DBT BLT 420$ ; If LT then crash MOV R1,R0 ; Copy high order size BIS R2,R0 ; Check for no blocks BNE 430$ ; We're OK 420$: BPT ; Breakpoint 430$: TST R1 ; Test result for following branch .ENDC ; DF D$$BUG!X$$DBT ; ; If the high order remaining blocks is non-zero, we can always finish ; the complete transfer on this file, since the largest possible ; transfer is 64K bytes. ; BNE 440$ ; If high order not zero, complete transfer ; ; Now calculate how much of the transfer we can perform now. ; MOV U.CNT(R5),R0 ; Get remaining transfer size ADD #777,R0 ; Round up to next block ; ; We use the following instead of ASH because it is faster (honest!) ; ROR R0 ; Number of blocks in high byte CLRB R0 ; Clear low order garbage SWAB R0 ; Number of blocks remaining in transfer CMP R2,R0 ; Compare blocks left with transfer size BHIS 440$ ; If his then we can finish entire transfer MOV R2,R1 ; We can transfer this many blocks ; ; Update the base LBN offset in the UCB for the next portion of the ; transfer. Note that this offset is NOT updated if we can complete ; the entire operation at this time. ; ADD R2,U.XLBN+2(R5) ; Add transfer size in blocks to base LBN ADC U.XLBN(R5) ; Propagate carry SWAB R1 ; Convert blocks back to bytes ROL R1 ; Remaining bytes in file BR 450$ ; Proceed 440$: MOV U.CNT(R5),R1 ; Perform remaining amount of transfer 450$: MOV R1,I.PRM+4(R3) ; Store number of bytes in I/O packet SUB R1,U.CNT(R5) ; Subtract from total transfer size ; ; We are through with the secondary block now. Save the current secondary ; block pointer in case we need it to do another segment, and retrieve ; the UCB address of the target disk. ; MOV R4,U.CTLP+2(R5) ; Save secondary block address MOV X.UCB(R4),R4 ; Get the target device UCB address MOV R4,I.UCB(R3) ; Store in the I/O packet ; ; Now convert the current buffer address to an APR 6 bias and displacement ; for non-NPR devices. For 18 bit devices, put the high order two bits ; in bits 4:5, and for the others (11/70 type devices), we're already ; set. ; MOV U.BUF(R5),R0 ; Get high order buffer address MOV U.BUF+2(R5),R1 ; Get low order buffer address BITB #UC.NPR,U.CTL(R4) ; NPR device? BNE 480$ ; If ne yes, no major conversion necessary SWAB R0 ; Get high bits into low byte ASHC #10.,R0 ; Calculate bias ASHC #-10.,R1 ; Calculate displacement ADD #140000,R1 ; Make it an APR 6 displacement BR 490$ ; And proceed 480$: .IF NDF M$$EXT ASH #-4.,R0 ; Put bits <17:18> into <4:5> .ENDC ; NDF M$$EXT 490$: MOV R0,I.PRM(R3) ; Store high order buffer address MOV R1,I.PRM+2(R3) ; Store low order buffer address ADD I.PRM+4(R3),U.BUF+2(R5) ; Update the buffer address ADCB U.BUF+1(R5) ; Propagate to high order address ; ; The last killer. The target device we are about to dispatch to may ; be shadowed. This may pose a problem as an ML node will not exist ; at this time (ML nodes are allocated in $DRQIO, and we're way past ; that step!). Hence, the following code is pirated from DRQIO to ; create an ML node. ; MOV R4,R5 ; Copy UCB address of target device MOV U.UMB(R5),R2 ; Get UMB address for target device BEQ 600$ ; If eq then no UMB BIT #MS.MDA,M.STS(R2) ; Can we allocate new nodes? BNE 600$ ; If ne no MOV #ML.LGH,R1 ; Get node length CALL $ALOCB ; Allocate an ML node BCC 500$ ; If cc then we got one ; ; As commented in DRQIO, it's a shame we have to kill the I/O simply ; because we could not allocate an ML node. Most of the requests will ; be reads, which will be satisfied by the primary I/O. All well... ; MOV #IE.UPN&377,R0 ; Insufficient DSR JMP VFALT ; And terminate I/O 500$: MOV R0,R1 ; Copy ML node address CLR (R0)+ ; Clear link word MOV #+ML.LGH,(R0)+ ; Set length and type CLR (R0)+ ; ML.DNC/unused; set done count MOV R3,(R0)+ ; Store primary I/O packet address MOV U.UMB(R5),R0 ; Retrieve UMB address MOV M.LHD(R0),(R1) ; Link first node into this one MOV R1,M.LHD(R0) ; Make this node the first one ; ; Pass the I/O packet along to the next victim and return to caller ; ; Arguments to $DRQRQ: ; ; R1 Contains the I/O packet address ; R5 Contains the address of the UCB for the target device ; ; The driver for the target device is invoked. We regain control when ; the target device completes the I/O. ; 600$: MOV R3,R1 ; Copy I/O packet address .IF DF P$$LOG ; Copy the target I/O packet MOV I.IOSB(R3),R0 ; Get virtual device UCB address ADD #U.PKT2,R0 ; Point to the save area MOV #I.LGTH/2,R2 ; Words to copy 29500$: MOV (R3)+,(R0)+ ; Copy I/O packet SOB R2,29500$ ; Until done .ENDC ; DF P$$LOG CALLR $DRQRQ ; Dispatch the I/O and return ;+ ; ; ** VFRET -- Internal I/O completion routine ; ; This code is entered from the $IOFIN routine after the target device ; has completed the I/O packet (including any shadowing). ; ; Inputs from $IOFIN: ; ; R3 I/O Packet address ; R5 UCB address of target device ; I.PRM+6 First I/O status word returned from target device ; I.PRM+10 Second I/O status word returned from target device ; ; Required outputs: ; ; R5 must still contain the UCB address for the target device, ; as $IOFIN will return to the target driver. ; ; Processing proceeds as follows: ; ; Push the UCB address of the target device and push the address ; of an internal routine to restore the target UCB address before ; return is made to $IOFIN. This allows us to propagate ourselves ; while ensuring that the target driver will be properly restored. ; ; If an I/O error was returned, then terminate this I/O ; with the I/O status and the actual bytes transfered. ; ; If the I/O completed successfully, determine if we have completed ; the entire transfer. If not, branch back to label VFNEXT to initiate ; the next part of this transfer. If we have finished everything, ; restore the I/O packet, call $IODON, and look for more work to do. ; VFRET:: MOV R5,-(SP) ; Save the target UCB address MOV #VFRST,-(SP) ; Force driver exit to restore routine MOV I.IOSB(R3),R5 ; Recover our UCB address ADD I.PRM+10(R3),U.ISB2(R5) ; Accumulate actual bytes transferred MOV I.PRM+6(R3),R0 ; Get the returned I/O status CMPB #IS.SUC,R0 ; Successful status returned? BNE VFDON ; If ne no - terminate I/O TST U.CNT(R5) ; Check for more work to do BEQ VFDON ; If eq then we have finished I/O MOV U.CTLP(R5),KISAR6 ; Map secondary block MOV U.CTLP+2(R5),R4 ; Get secondary block displacement pointer ADD #X.LEN,R4 ; Point to the next file block JMP VFNEXT ; And resume I/O processing VFDON: MOV U.ISB2(R5),R1 ; Get second I/O status word MOV R5,I.UCB(R3) ; Restore our UCB in I/O packet MOV U.IOSB(R5),I.IOSB(R3) ; Restore I/O status virtual address MOV U.IOSB+2(R5),I.IOSB+2(R3) ; Restore I/O status bias MOV U.IOSB+4(R5),I.IOSB+4(R3) ; Restore I/O status displacement .IF DF P$$LOG MOV R5,-(SP) ; Save our UCB address MOV R0,U.ISTS(R5) ; Save first I/O status word MOV R1,U.ISTS+2(R5) ; Save second I/O status word TST SERVER ; Anybody there? BEQ 29600$ ; If eq no MOV R5,U.PUCB(R5) ; Store our UCB for kicks MOV R5,R0 ; Copy UCB address ADD #U.MLND,R0 ; Point to the save area CLR (R0) ; Assume one does not exist CALL $SHFND ; Look for ML node BCS 29510$ ; If CS then not there MOV R4,(R0)+ ; Store address of ML node in link MOV #-1,R1 ; Number of words to move TST (R4)+ ; Skip over link word 29505$: MOV (R4)+,(R0)+ ; Store ML node SOB R1,29505$ ; Until done 29510$: MOV #U.PKLN,R1 ; Length of packet ADD #77,R1 ; Round up ASH #-6,R1 ; Convert to 32 word blocks CALL $ALSEC ; Allocate the pool BCS 29600$ ; All well, give up MOV KISAR6,-(SP) ; Save APR6 MOV R0,KISAR6 ; Map the packet MOV #140000,R0 ; Point to the packet MOV R5,R1 ; Copy the UCB ADD #U.SPKT,R1 ; Point to the packet MOV #U.PKLN/2,R2 ; Length of packet in words 29550$: MOV (R1)+,(R0)+ ; Copy the packet SOB R2,29550$ ; Until done MOV KISAR6,R1 ; Get packet address MOV (SP)+,KISAR6 ; Restore APR6 MOV SERVER,R0 ; Get TCB address of server ADD #T.RCVL,R0 ; Point to receive queue listhead CALL $QSPIF ; Queue the packet MOV SERVER,R0 ; Get the TCB address again CALL $EXRQN ; Request the task 29600$: MOV (SP)+,R5 ; Get back UCB address MOV U.ISTS(R5),R0 ; Get back first I/O status word MOV U.ISTS+2(R5),R1 ; Get back second I/O status word .ENDC ; DF P$$LOG BR VFFIN ; Finish I/O VFALT: CLR R1 ; Return I/O status word 2 ; ; Arguments to $IODON: ; ; R0 - First I/O status word ; R1 - Second I/O status word ; R5 - UCB address of device ; S.PKT is assumed to contain the I/O packet address ; VFFIN: CALL $IODON ; Finish I/O JMP VFINI1 ; And look for more work to do ; ; ** VFRST -- Restore UCB address and return to $IOFIN ; ; This code is executed as the final step of the internal I/O completion ; routine. ; ; Inputs: (SP) Address of target device UCB ; 2(SP) Return address of caller ($IOFIN) ; VFRST: MOV (SP)+,R5 ; Restore UCB address RETURN ; And return to caller ; ; ** VFUCB -- Unit online-offline entry point ; ; So why do we need this? Well, if we have supplied support for ; virtual disks with whatever name the user chooses, we have to do ; some checks when VF0: is taken offline to make sure we won't have ; "orphan" databases. ; ; Upon entry, our job is to scan the Virtual Disk list, looking for devices ; which have current I/O activity. If they do, then we reject the ; offline transition. If all devices are quiescent, then we take them ; offline and clear the dispatch table address. ; VFUCB: ROL R0 ; Save the carry bit MOV U.DCB(R5),R4 ; Get DCB address for this unit CMP #<"VF>,D.NAM(R4) ; Is it a VF unit? BNE 200$ ; If ne no, accept request TST D.UNIT(R4) ; Is it VF0:? BNE 200$ ; If ne no, accept request ROR R0 ; Shift the carry back BCC 100$ ; If cc then going online ; ; Offline transition. Check for all offspring virtual units being offline ; MOV R5,R1 ; Copy UCB address 10$: MOV U.VLNK(R1),R1 ; Get next UCB BEQ 20$ ; If eq then all are quiet BITB #US.OFL,U.ST2(R1) ; Is this unit offline? BNE 10$ ; If ne yes - proceed to next unit MOVB #IE.RSU,$SCERR ; Mark shareable resource in use BR 200$ ; And return failure ; ; All virtual units are quiescent. Now mark all offspring units have ; having "unloaded" drivers. ; 20$: MOV U.SCB(R5),R1 ; Get the SCB address CLRB S.STS(R1) ; Mark VF0: unit as not busy MOV R5,R1 ; Copy UCB address 30$: MOV U.VLNK(R1),R1 ; Get next UCB BEQ 200$ ; If eq then all done MOV U.DCB(R1),R0 ; Get the DCB CLR D.PCB(R0) ; And clear the driver PCB cell CLR D.DSP(R0) ; Clear driver dispatch cell BR 30$ ; Back for more ; ; Going online. "Load" the drivers of the offspring virtual units ; 100$: MOV U.SCB(R5),R1 ; Get the SCB address INCB S.STS(R1) ; Show VF0: as "busy" MOV R5,R1 ; Copy the UCB address 110$: MOV U.VLNK(R1),R1 ; Get next unit BEQ 200$ ; If eq then all done MOV U.DCB(R1),R0 ; Get DCB address MOV D.PCB(R4),D.PCB(R0) ; Point the device to our driver MOV D.DSP(R4),D.DSP(R0) ; Point to our dispatch table BR 110$ ; And proceed 200$: .IF DF P$$LOG CLR SERVER ; Assume no server alive MOV #SERNAM,R3 ; Point to server task name CALL $SRSTD ; Search for it BCS 29900$ ; Not found MOV R0,SERVER ; Save the TCB address 29900$: .ENDC ; DF P$$LOG RETURN ; Return to caller ; ; Alternate driver entry points. ; ; These are all effective no-op's, as the conditions will either be ; handled by the target devices or serve no useful function ; VFPWF: ; Power fail entry point: let others handle VFCAN: ; I/O Kill entry point: we CAN'T do anything! VFOUT: ; Timeout entry point: let others handle VFKRB: ; Controller online/offline: we ain't got one RETURN .END