.title VDDRIVER .sbttl VMS Virtual Disk Driver .ident /092188/ ; ;+ ;***** ; ; VDDRIVER.MAR - Implements a "virtual disk" device using a contiguous ; container file residing on a real physical disk. All I/O requests ; for the virtual disk are re-mapped to equivalent I/O functions ; to be performed by the physical disk driver. ; ; The basic concept of this driver has been inspired by R. Stammerjohn, ; et. al., who gave VD to many RSX systems, and G. Everhart who made ; the conversion to VMS. Some basic changes in logic from the original ; VMS VDDRIVER submitted through the VAX/VMS SIG DECUS symposium tape ; include: ; ; 1) Handle segmented, virtual I/O ; 2) Use IO$_SETMODE to associate VD with physical disk and ; require issuing process to have opened the container file ; 3) Set volume valid via EXE$LCLDSKVALID routine ; ;- ;***** ; ; Paul Sorenson ; AEP/Engineering Computer Support Center ; Columbus, OH 43215 ; ;***** ; ; Update 9/7/88 -- PRS; Change from using extended IRP's to track ; active, segmented virtual I/O to using a "pending I/O ; identification block" (PIB) linked to pending/free queues ; in local data space of driver. ; ; Update 9/14/88 -- PRS; Add support for returning file-id of container ; file using special IO$_SENSEMODE function. ; ; Update 9/21/88 -- PRS; Add support for virtual disks on virtual disks. ; ;***** ;-- .page .SBTTL External and local symbol definitions ; ; External symbols ; $CANDEF ; Cancel reason codes $CCBDEF ; Channel control block $CRBDEF ; Channel request block $DCDEF ; Device classes and types $DDBDEF ; Device data block $DDTDEF ; Driver dispatch table $DEVDEF ; Device characteristics $DPTDEF ; Driver prologue table $DYNDEF ; Dynamic data structures $FCBDEF ; File control block $IDBDEF ; Interrupt data block $IODEF ; I/O function codes $IPLDEF ; Hardware IPL definitions $IRPDEF ; I/O request packet $PRDEF ; Processor registers $PRTDEF ; Page protection $PSLDEF ; Processor status longword $SSDEF ; System status codes $UCBDEF ; Unit control block $VECDEF ; Interrupt vector block $WCBDEF ; Window control block VD$IODEF VD$PIBDEF VD$UCBDEF ; ; Local symbols ; ; ; Argument list (AP) offsets for device-dependent QIO parameters ; P1 = 0 ; First QIO parameter P2 = 4 ; Second QIO parameter P3 = 8 ; Third QIO parameter P4 = 12 ; Fourth QIO parameter P5 = 16 ; Fifth QIO parameter P6 = 20 ; Sixth QIO parameter ; ; Other constants ; VD_FIPL == IPL$_SYNCH ; default fork IPL VD_BLKSIZ == 512 ; Default buffer size ; ;DEBUG=0 ; UNCOMMENT LINE TO ENABLE XDELTA BREAKPOINTS ; ; .page .sbttl Standard tables ; ; Driver prologue table...contents established along the lines of a ; standard VMS disk driver though many of the definitions are unused ; or may even be altered at runtime depending on the disk assigned. ; DPTAB - ; DPT-creation macro END=VD_END,- ; End of driver label ADAPTER=NULL,- ; Adapter type UCBSIZE=,- ; Length of UCB NAME=VDDRIVER,- ; Driver name UNLOAD=VD_UNLOAD ; Routine to unload driver MAXUNITS=32 ; Allow up to 32 VDs DPT_STORE INIT ; Start of load ; initialization table DPT_STORE DDB,DDB$L_ACPD,L,- ; Default ACP name <^A\F11\> DPT_STORE DDB,DDB$L_ACPD+3,B,DDB$K_PACK ; ACP class DPT_STORE UCB,UCB$B_FIPL,B,VD_FIPL ; Device fork IPL DPT_STORE UCB,UCB$B_DIPL,B,VD_FIPL ; Device interrupt IPL (dummy) DPT_STORE UCB,UCB$L_DEVCHAR,L,- ; Device characteristics ; random access DPT_STORE UCB,UCB$L_DEVCHAR2,L,- ; Device characteristics ; prefix name with "node$" DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_DISK ; Device class DPT_STORE UCB,UCB$B_DEVTYPE,B,DT$_FD1 ; Device type DPT_STORE UCB,UCB$W_DEVBUFSIZ,W,- ; Default buffer size VD_BLKSIZ DPT_STORE UCB,UCB$B_SECTORS,B,0 ; Number of sectors/track DPT_STORE UCB,UCB$B_TRACKS,B,0 ; Number of tracks/cylinder DPT_STORE UCB,UCB$W_CYLINDERS,W,0 ; Number of cylinders ; (set at runtime) DPT_STORE UCB,UCB$W_DEVSTS,W,- ; Device status ; inhibit log to phy conv DPT_STORE REINIT ; Start of reload ; initialization table DPT_STORE DDB,DDB$L_DDT,D,VD$DDT ; Address of DDT DPT_STORE CRB,- ; Address of controller CRB$L_INTD+VEC$L_INITIAL,- ; initialization routine D,VD_CTRL_INIT DPT_STORE CRB,- ; Address of device CRB$L_INTD+VEC$L_UNITINIT,- ; unit initialization D,VD_UNIT_INIT ; routine DPT_STORE END ; End of initialization ; tables ; ; Driver dispatch table ; DDTAB - ; DDT-creation macro DEVNAM=VD,- ; Name of device START=VD_STARTIO,- ; Start I/O routine FUNCTB=VD_FUNCTABLE ; FDT address ; ; Function decision table ; VD_FUNCTABLE: FUNCTAB ,- ; Valid I/O functions ; mount volume FUNCTAB ,- ; Buffered I/O functions ; mount volume FUNCTAB VD_ALIGN,- ; verify buffer word count alignment ; write virtual block FUNCTAB +ACP$READBLK,- ; read functions ; read virtual block FUNCTAB +ACP$WRITEBLK,- ; write functions ; write virtual block FUNCTAB +ACP$ACCESS,- ; access functions ; create file / directory entry FUNCTAB +ACP$DEACCESS,- ; deaccess function ; deaccess file FUNCTAB +ACP$MODIFY,- ; modify functions ; modify file attributes FUNCTAB +ACP$MOUNT,- ; mount function ; mount volume FUNCTAB +EXE$LCLDSKVALID,- ; local disk valid functions ; available FUNCTAB +EXE$ZEROPARM,- ; zero parameter functions ; drive clear FUNCTAB VD_SENSEMODE,- ; special SENSEMODE FDT ; sense mode FUNCTAB +EXE$SENSEMODE,- ; if VD_SENSEMODE doesn't do it ; sense mode FUNCTAB VD_SETMODE,- ; special SETMODE FDT ; set mode FUNCTAB +EXE$SETCHAR,- ; if VD_SETMODE doesn't do it ; set mode ; ; .page .sbttl Local Data Structures ; ;+ ; ; *** Local data structures required by the virtual disk driver are ; placed at this point. The decision to place these structures ; here was made because the virtual disk driver may be recalled ; by the VMS I/O post-processing routine (IOC$IOPOST) to handle ; completion of segmented virtual I/O and there will be no driver ; "context" (UCB address) established at this time. ; ; If these structures were maintained in each UCB, the virtual disk ; driver would still have to search each list in each active UCB in ; order to resolve the proper unit the I/O was associated with. ; UCB:: ; address of first UCB linked to driver .BLKL 1 PENDQFL:: ; queue list head for pending I/O requests .BLKL 1 PENDQBL:: .BLKL 1 FREEQFL:: ; queue list head for free PIBs .BLKL 1 FREEQBL:: .BLKL 1 .page .sbttl Controller Initialization Routine ; ;+ ; ; *** VD_CTRL_INIT -- Readies "controller" for I/O operations. ; ; Functional description: ; ; The operating system calls this routine in 3 places: ; ; at system startup ; during driver loading and reloading ; during recovery from a power failure ; ; Since there is no physical controller for the cache driver, ; this routine is only responsible for initializing the local driver ; data structures, PENDQxL and FREEQxL. The PENDQxL and FREEQxL ; fields define two queue list heads for maintaing the "pending I/O ; identificiation block" (PIB) structures required to save/restore ; information regarding each active I/O request. ; ; *** ; ; On Entry: ; ; R4 = address of the CSR (controller status register) ; R5 = address of the IDB (interrupt data block) ; R6 = address of the DDB (device data block) ; R8 = address of the CRB (channel request block) ; IPL = IPL$_POWER ; ; returns: ; ; (this routine must preserve all registers except R0-R3) ; ;- VD_CTRL_INIT:: ; Initialize controller .IIF DEFINED DEBUG, JSB G^INI$BRK MOVL DDB$L_UCB(R6),UCB ; save pointer to first UCB MOVAL PENDQFL,PENDQFL ; zero pending queue list head MOVAL PENDQFL,PENDQBL MOVAL FREEQFL,FREEQFL ; zero free queue list head MOVAL FREEQFL,FREEQBL RSB ; Return ; .page .sbttl Unit initialization routine ; ;+ ; ; *** VD_UNIT_INIT -- Readies unit for I/O operations ; ; Functional description: ; ; The operating system calls this routine after calling the ; controller initialization routine: ; ; at system startup ; during driver loading (NOT RELOADING !!) ; during recovery from a power failure ; ; The contents of special UCB fields that must be initialized are ; established by this routine. ; ; *** ; ; On entry: ; ; R4 - address of the CSR (controller status register) ; R5 - address of the UCB (unit control block) ; ; returns: ; ; (The routine must preserve all registers except R0-R3) ; ;- ; VD_UNIT_INIT:: ; Initialize unit .IIF DEFINED DEBUG, JSB G^INI$BRK MOVL UCB$L_DDB(R5),R0 ; pickup DDB address MOVL DDB$L_UCB(R0),UCB ; then save pointer to first unit CLRL UCB$L_VD_DSKUCB(R5) ; clear host disk UCB address PUSHR #^M ; save registers MOVC5 #0,(R5),#0,#,UCB$T_VD_INFO(R5) ; zero out any stale information POPR #^M ; restore registers BISW #UCB$M_ONLINE,UCB$W_STS(R5) ; Set unit online on success RSB ; Return ; .page .sbttl Driver Unload Routine ; ;+ ; ; *** VD_UNLOAD -- Called by VMS when the driver is being reloaded. ; Though the documentation is sketchy, it appears that the ; driver may block unloading/reloading by returning an error ; indication to the caller. This will be done if any ; virtual disk is active. ; ; !!!!!NOTE!!!!! ; Due to the current method of tracking active, segmented virtual ; I/O, the non-paged pool allocated and linked to the FREEQ and ; PENDQ lists will be lost (until next system boot) with each ; reload of the driver. Ultimately, this driver should either be ; flagged as not reloadable or code must be added to consolidate ; the PIBs and deallocate them (as IRPs) to free pool. ; ; *** ; ; On entry: ; ; ??? ; ; returns: ; ; R0 = SS$_NORMAL if all VD units are inactive, ; SS$_DEVACTIVE otherwise ; ;- ; VD_UNLOAD:: .IIF DEFINED DEBUG, JSB G^INI$BRK MOVL UCB,R0 ; point to first UCB 2$: BBS #DEV$V_MNT,UCB$L_DEVCHAR(R0),8$ ; branch if virtual disk is still mounted BBS #UCB$V_VALID,UCB$L_STS(R0),8$ ; or volume valid MOVL UCB$L_LINK(R0),R0 ; step to next UCB BNEQ 2$ ; loop til done MOVAL PENDQFL,R0 ; check the pending queue CMPL R0,(R0) ; queue empty ?? BNEQ 8$ ; branch if not MOVZWL #SS$_NORMAL,R0 ; setup successful status BRB 9$ 8$: MOVZWL #SS$_DEVACTIVE,R0 ; setup error status 9$: RSB ; .page .sbttl SENSEMODE/SENSECHAR FDT Routine ; ;+ ; ; *** VD_SENSEMODE -- Called to pre-process the IO$_SENSEMODE/IO$_SENSECHAR ; functions. Only one modifier is accepted: ; ; IO$M_VD_GETINFO - Returns information regarding the host ; disk and container file. ; ; The user's buffer defined by QIO parameters P1 (address) and ; P2 (size) will be filled with the following information: ; ; 1) Host disk device name (counted string); 16 bytes ; 2) Host disk device unit number; 2 bytes ; 3) Host container file's file-id; 6 bytes ; 4) Size of virtual disk in blocks; 4 bytes ; 5) Starting LBN of file; 4 bytes ; ; The I/O status block for the IO$_SENSEMODE!IO$M_VD_GETINFO function ; contains: ; ; +----------------------+----------------------+ ; |info buffer byte count| Status (SS$_NORMAL) | ; +----------------------+----------------------+ ; | Host disk's UCB address | ; +----------------------+----------------------+ ; ; If the IO$M_VD_GETINFO modifier is not specified, the contents of ; UCB$L_DEVDEPEND (sectors/tracks/cylinders) is returned by the ; standard EXE$SENSEMODE FDT routine in the second long word ; of the I/O status block: ; ; +----------------------+----------------------+ ; | 0 | Status (SS$_NORMAL) | ; +----------------------+-----------+----------+ ; | # cylinders | # tracks | # sectors| ; +----------------------+-----------+----------+ ; ; *** ; ; On Entry: ; ; R0-R2 - scratch registers ; R3 - address of the IRP (I/O request packet) ; R4 - address of the PCB (process control block) ; R5 - address of the UCB (unit control block) ; R6 - address of the CCB (channel control block) ; R7 - bit number of the I/O function code ; R8 - address of the FDT table entry for this routine ; R9-R11 - scratch registers ; AP - address of the 1st function dependent QIO parameter ; ; returns: ; ; ;- VD_SENSEMODE:: BBS #IO$V_VD_GETINFO,IRP$W_FUNC(R3),2$ ; branch if special function RSB ; else, return through EXE$SENSEMODE 2$: PUSHR #^M ; free registers MOVL P1(AP),R0 ; pickup user buffer address BEQL 13$ ; branch if undefined MOVL P2(AP),R1 ; size in bytes BEQL 13$ ; branch if undefined JSB G^EXE$READCHK ; verify write access to user's buffer CLRL R2 ; assume virtual disk is not assigned TSTL UCB$L_VD_DSKUCB(R5) ; virtual disk active ?? BEQL 12$ ; branch if not, zero user buffer MOVL #UCB$K_VD_LENGTH-UCB$T_VD_INFO,R2 ; get size of UCB info array 12$: MOVC5 R2,UCB$T_VD_INFO(R5),#0,R1,(R0) ; copy info to user buffer 13$: POPR #^M ; restore registers MOVZWL #SS$_NORMAL,R0 ; assume success MOVL #UCB$K_VD_LENGTH-UCB$T_VD_INFO,R1 ; get # bytes of statistics CMPL P2(AP),R1 ; check if data was truncated BGEQ 20$ ; branch if not MOVZWL #SS$_BUFFEROVF,R0 ; reset status MOVL P2(AP),R1 ; and return user buffer size 20$: INSV R1,#16,#16,R0 ; insert transfer byte count MOVL UCB$L_VD_DSKUCB(R5),R1 ; setup second I/O status long word VD_FINISHIO:: JMP G^EXE$FINISHIO ; and complete the I/O ; .page .sbttl SETMODE/SETCHAR FDT Routine ; ;+ ; ; *** VD_SETMODE -- Called to pre-process the IO$_SETMODE/IO$_SETCHAR ; functions. There are two major functions controlled by I/O ; function modifiers: ; ; IO$M_VD_STARTUP - Associates file with virtual disk device ; IO$M_VD_SHUTDOWN- Deassociates file from virtual disk device ; (additional modifiers may be added for future functionality) ; ; All SETMODE/SETCHAR function processing is performed immediately ; and may necessitate raising the IPL to fork level to synchronize ; access with common/system data structures. ; ; ; *** ; ; On Entry: ; ; R0-R2 - scratch registers ; R3 - address of the IRP (I/O request packet) ; R4 - address of the PCB (process control block) ; R5 - address of the UCB (unit control block) ; R6 - address of the CCB (channel control block) ; R7 - bit number of the I/O function code ; R8 - address of the FDT table entry for this routine ; R9-R11 - scratch registers ; AP - address of the 1st function dependent QIO parameter ; ; returns: ; ; ;- VD_SETMODE:: MOVZWL #SS$_ILLIOFUNC,R0 ; assume error BICL3 #^C,IRP$W_FUNC(R3),R1 ; pickup valid modifiers only BEQL 10$ ; pass on to EXE$SETMODE if none FFS #IO$V_FMODIFIERS,#IO$S_FMODIFIERS,R1,R2 ; determine major modifier bit BBCC R2,R1,5$ ; clear major modifier bit 5$: TSTW R1 ; more than one major modifier ?? BNEQ 10$ ; branch if more than one, illegal CASEB R2,#IO$V_FMODIFIERS,#2 8$: .word STARTUP-8$ .word SHUTDOWN-8$ 10$: RSB ; return to dispatch to EXE$SETMODE ;* BRB VD_FINISHIOC ; * OR * complete I/O with error ; ; *** Shutdown virtual disk. Device must be dismounted but still associated ; with a physical disk UCB for successful operation. ; SHUTDOWN: MOVZWL #SS$_DEVINACT,R0 ; assume virtual disk inactive TSTL UCB$L_VD_DSKUCB(R5) ; VDDRIVER assoc with physical disk ?? BEQL VD_FINISHIOC ; branch if not MOVZWL #SS$_DEVMOUNT,R0 ; assume virtual disk still mounted BBS #DEV$V_MNT,UCB$L_DEVCHAR(R5),VD_FINISHIOC ; branch if it is CLRL UCB$L_VD_DSKUCB(R5) ; dissassociate VDDRIVER from disk CLRL UCB$L_DEVDEPEND(R5) CLRL UCB$L_MAXBLOCK(R5) MOVZWL #SS$_NORMAL,R0 ; set successful status ; VD_FINISHIOC:: JMP G^EXE$FINISHIOC ; complete the I/O ; ; *** Startup virtual disk. Issuing process must have assigned a channel ; and opened the virtual disk container file. The CCB, WCB, and FCB ; established by VMS for the container file will be referenced ; by this driver to resolve: ; ; CCB$L_UCB contains physical disk's UCB address ; WCB$W_COUNT1/WCB$L_LBN determine the LBNs allocated to the file ; FCB$L_SIZE establishes the size of the file/virtual disk ; ; As a protection scheme, the process establishing a virtual disk ; should modify the container file's protection masks to block ; all attempts to delete the file. Conversely, the protection mask ; should be reset to allow file deletion once the virtual disk ; is deassigned. ; ; STARTUP: MOVZWL #SS$_DEVACTIVE,R0 ; assume error TSTL UCB$L_VD_DSKUCB(R5) ; VD already assoc with disk ?? BNEQ VD_FINISHIOC ; branch if yes ; ; *** Following code extracted from EXE$QIOREQ to validate an I/O channel ; and determine the UCB address of the device assigned to the channel. ; This was chosen to allow the SYS$ASSIGN system service to do all ; the work involved with logical name translation and scanning of ; the device tables. ; MOVZWL #SS$_BADPARAM,R0 ; assume error BICL3 #<^XFFFF0000!>,P1(AP),R1 ; get channel assigned to disk BEQL VD_FINISHIOC ; branch if illegal CMPW R1,G^CTL$GW_CHINDX ; legal number ?? BGTRU VD_FINISHIOC ; branch if not MNEGL R1,R1 ; convert to channel index ADDL G^CTL$GL_CCBBASE,R1 ; get address of CCB MOVZWL #SS$_NOPRIV,R0 ; assume error MOVPSL R2 ; read current PSL EXTZV #PSL$V_PRVMOD,#PSL$S_PRVMOD,R2,R2 ; extract previous mode field CMPB R2,CCB$B_AMOD(R1) ; caller have priv to access channel ?? BGTR VD_FINISHIOC ; branch if not, finish I/O ; ; *** Verify that device assigned to channel is valid target for virtual ; disk and that the container file has been accessed by the process ; so this driver can get important information from the window ; and file control blocks. ; MOVZWL #SS$_NOTFILEDEV,R0 ; assume error MOVL CCB$L_UCB(R1),R2 ; pickup disk's UCB address BBC #DEV$V_FOD,UCB$L_DEVCHAR(R2),VD_FINISHIOC ; branch if not file device MOVZWL #SS$_NOSUCHFILE,R0 ; assume error MOVL CCB$L_WIND(R1),R1 ; pickup window control block BEQL VD_FINISHIOC ; branch if file not opened BBS #WCB$V_NOTFCP,WCB$B_ACCESS(R1),VD_FINISHIOC ; ?? this check is from IOC$MAPVBLK ?? MOVL WCB$L_FCB(R1),R0 ; pickup file control block MOVL FCB$L_STLBN(R0),UCB$L_VD_LBN(R5) ; save starting LBN MOVL FCB$L_FILESIZE(R0),UCB$L_VD_SIZE(R5) ; and size of file MOVL FCB$W_FID_NUM(R0),UCB$W_VD_FNUM(R5) ; save file, sequence and MOVW FCB$W_FID_RVN(R0),UCB$W_VD_FRVN(R5) ; relative volume number of file ; ; *** May want to add a check for contiguous file here by verifying that ; the WCB retrieval pointers represent adjacent LBNs...could also ; verify that entire file is mapped by the retrieval pointers. ; ;* MOVL #SS$_FILNOTCNTG,R0 ; assume error ;* ...etc... ; ; ; *** Check for virtual disks on virtual disks. ; MOVL UCB$L_DDB(R5),R0 ; point to DDB MOVL DDB$L_UCB(R0),R0 ; to find 1st UCB BISW #UCB$M_VD_RECURS,UCB$W_DEVSTS(R5) ; assume file is on a virtual disk 40$: CMPL R2,R0 ; host disk UCB on VDDRIVER ?? BEQL 45$ ; branch if yes MOVL UCB$L_LINK(R0),R0 ; step to next UCB BNEQ 40$ ; branch if defined, check it BICW #UCB$M_VD_RECURS,UCB$W_DEVSTS(R5) ; show virtual disk on physical disk 45$: ; ; *** At this point, everything should be ready for mount/init of the virtual ; disk. Show VD active by defining address of physical disk's UCB, ; compute fake geometry for the virtual disk, and save disk device ; name and unit number for IO$_SETMODE!IO$M_GETINFO. ; MOVB UCB$B_FIPL(R2),UCB$B_FIPL(R5) ; force VD's fork IPL to match hosts MOVL R2,UCB$L_VD_DSKUCB(R5) ; save physical disk's UCB address MOVL UCB$L_VD_SIZE(R5),R0 ; pickup size of container file DIVL3 #^X10000,R0,R1 ; divide it by max # cylinders +1 INCL R1 ; add 1 to compute # sectors/track MOVB R1,UCB$B_SECTORS(R5) ; save # sectors/track MOVB #1,UCB$B_TRACKS(R5) ; force tracks/cylinder to 1 DIVL R1,R0 ; compute truncated # cylinders MOVW R0,UCB$W_CYLINDERS(R5) ; save # cylinders MULL2 R1,R0 ; compute actual # blocks on VD MOVL R0,UCB$L_MAXBLOCK(R5) ; save it for VMS MOVL R0,UCB$L_VD_SIZE(R5) ; and reset size for IO$M_GETINFO MOVL UCB$L_MAXBCNT(R2),UCB$L_MAXBCNT(R5) ; match VDs max byte count with disk's ADDL3 UCB$L_DDB(R2),#DDB$T_NAME,R0 ; point to disk device name MOVQ (R0)+,UCB$T_VD_NAME(R5) ; copy device name to VD's UCB MOVQ (R0),UCB$T_VD_NAME+8(R5) MOVW UCB$W_UNIT(R2),UCB$W_VD_UNIT(R5) ; copy device unit number to VD's UCB MOVZWL #SS$_NORMAL,R0 ; show success 69$: BRW VD_FINISHIOC ; complete the request ; .sbttl Read/Write FDT Routine ; ; *** VD_ALIGN -- Verifies word alignment of transfer byte count for all ; disk reads/writes. While some disk hardware may be able to handle ; odd byte counts, the typical UNIBUS/Q-Bus hardware registers ; transfer words only. ; ; VD_ALIGN:: BLBS 4(AP),2$ ; branch on odd byte count RSB ; return to continue I/O 2$: MOVZWL #SS$_IVBUFLEN,R0 ; set QIO service return code VD_ABORTIO:: JMP G^EXE$ABORTIO ; abort I/O request ; .sbttl Start I/O Routine ; ; *** VD_STARTIO -- Called to initiate the I/O request for the virtual disk. ; All I/O transfers are remapped to the appropriate physical block ; within the container file prior to being passed on to the ; host disk's driver which actually performs the operation. ; Special considerations must be given to segmented virtual and ; logical I/O operations: ; ; Virtual I/O transfer requests exceeding UCB$L_MAXBCNT (default 512*127) ; and/or not mapped by contiguous disk LBNs will be segmented. The ; context of each segmented I/O must remain with the virtual disk ; driver to correctly map all segments of the transfer. To accomplish ; this requirement, any segmented virtual I/O request will be associated ; with a "pending I/O identification block" (PIB) linked to the ; PENDQFL list. The PIB will contain all the information required ; to restore the context of both the virtual disk driver and the ; I/O request packet fields when the driver is recalled by the VMS ; I/O post processing routine (IOC$IOPOST). ; ; Logical I/O requests are limited to UCB$L_MAXBCNT (default 512*127) ; byte transfers and will be segmented if required. ACP$xxx FDT ; routines prepare for segmented logical I/O by saving the starting ; LBN from IRP$L_MEDIA in IRP$L_SEGVBN which IOC$IOPOST will then ; use to continue the transfer. Once IRP$L_MEDIA and IRP$L_SEGVBN ; are remapped to the correct host disk LBNs, the virtual disk driver ; can hand the request to the host driver and is done with the request. ; ; Physical I/O requests are apparently never segmented. The problem ; in supporting these types of requests is in determining whether the ; host disk expects the physical disk address in terms of ; cylinder/track/sector or logical block (DSA disks). ; ; VD_STARTIO:: MOVZWL #SS$_VOLINV,R0 ; assume error MOVL R5,R1 ; copy UCB address 2$: MOVL UCB$L_VD_DSKUCB(R1),R2 ; pickup host disk's UCB address BEQL VD_REQCOM ; kill request if undefined BBC #DEV$V_MNT,UCB$L_DEVCHAR(R2),VD_REQCOM ; kill request if host volume dismounted ;* BBC #UCB$V_VALID,UCB$W_STS(R2),VD_REQCOM ; kill request if host volume invalid BBC #UCB$V_VD_RECURS,UCB$W_DEVSTS(R1),10$ ; branch if virtual disk on real disk MOVL R2,R1 ; step to next UCB to verify that all BRB 2$ ; virtual/real disks in chain are mntd 10$: BBS #IRP$V_PHYSIO,IRP$W_STS(R3),12$ ; branch if physical I/O BBC #UCB$V_VALID,UCB$W_STS(R5),VD_REQCOM ; kill request if VD volume not valid 12$: EXTZV #IRP$V_FCODE,#IRP$S_FCODE,IRP$W_FUNC(R3),R1 ; pickup function code MOVZWL #SS$_NORMAL,R0 ; assume success CASEB R1,#IO$_UNLOAD,#IO$_AVAILABLE-IO$_UNLOAD 15$: .word UNLOAD-15$ ; UNLOAD .word NOP-15$ ; SEEK (unsupported) .word NOP-15$ ; DRIVE CLEAR (unsupported) .word NOP-15$ ; RECALIBRATE (unsupported) .word NOP-15$ ; RELEASE PORT (unsupported) .word NOP-15$ ; OFFSET HEADS (unsupported) .word NOP-15$ ; RETURN TO CENTER (unsupported) .word PACKACK-15$ ; PACK ACKNOWLEDGE .word NOP-15$ ; SEARCH (unsupported) .word NOP-15$ ; WRITE CHECK (unsupported) .word WRITEDATA-15$ ; WRITE DATA .word READDATA-15$ ; READ DATA .word NOP-15$ ; WRITE HEADER (unsupported) .word NOP-15$ ; READ HEADER (unsupported) .word NOP-15$ ; (place holder) .word NOP-15$ ; (place holder) .word AVAILABLE-15$ ; AVAILABLE ; NOP: VD_REQCOM:: CLRL R1 ; all I/O completions have 2nd status=0 REQCOM ; force request completion PACKACK: BISW #UCB$M_VALID,UCB$W_STS(R5) ; set software volume valid BRB VD_REQCOM ; and complete the request UNLOAD: AVAILABLE: BICW #UCB$M_VALID,UCB$W_STS(R5) ; clear software volume valid BRB VD_REQCOM ; and complete the request ; ALLOCPIBS: ; *** allocate an IRP and break it into PIBs PUSHL R3 ; free R3 MOVL #IRP$K_LENGTH,R1 ; get size of block desired JSB G^EXE$ALONONPAGED ; allocate IRP (R0-R3 destroyed) BLBC R0,VD_REQCOM ; branch on error, force I/O completion 4$: SUBL #PIB$K_LENGTH,R1 ; full PIB left in packet ?? BLEQ 9$ ; branch if not INSQUE (R2),FREEQFL ; link PIB to free list ADDL #PIB$K_LENGTH,R2 ; step to start of next PIB BRB 4$ ; and continue 9$: MOVL (SP)+,R3 ; recover IRP address ; WRITEDATA: READDATA: BBC #IRP$V_VIRTUAL,IRP$W_STS(R3),10$ ; branch if not virtual I/O CMPL IRP$L_BCNT(R3),IRP$L_OBCNT(R3) ; transfer count = orig request count ?? BEQL 10$ ; branch if yes, not segmented REMQUE @FREEQFL,R2 ; pull PIB from free list BVS ALLOCPIBS ; branch on error, get more PIBs MOVL R3,PIB$L_IRP(R2) ; save IRP address in PIB MOVL IRP$L_PID(R3),PIB$L_PID(R2) ; save PID MOVL R5,PIB$L_UCB(R2) ; save VDDRIVER's UCB address MOVAL VD_IOPOST,IRP$L_PID(R3) ; reset PID to call VD_IOPOST INSQUE (R2),@PENDQBL ; insert PIB at end of pending queue 10$: BICL #UCB$M_BSY,UCB$L_STS(R5) ; clear our busy indication INCL UCB$L_OPCNT(R5) ; show one more I/O "completed" 12$: ADDL UCB$L_VD_LBN(R5),IRP$L_MEDIA(R3) ; compute true starting LBN BBS #IRP$V_VIRTUAL,IRP$W_STS(R3),15$ ; branch if virtual I/O ADDL UCB$L_VD_LBN(R5),IRP$L_SEGVBN(R3) ; update segmented logical I/O start LBN 15$: BBC #UCB$V_VD_RECURS,UCB$W_DEVSTS(R5),18$ ; branch if virtual disk on real disk MOVL UCB$L_VD_DSKUCB(R5),R5 ; get host virtual disk UCB address BRB 12$ ; and offset LBN again 18$: MOVL UCB$L_VD_DSKUCB(R5),R5 ; recover host disk UCB address MOVL R5,IRP$L_UCB(R3) ; revector IRP to host UCB MOVL IRP$L_MEDIA(R3),R0 ; recover starting LBN JSB G^IOC$CVTLOGPHY ; convert LBN to physical address ; (R0-R2 destroyed) JMP G^EXE$INSIOQ ; and pass request to host driver ; .sbttl Segmented I/O Completion Handler ; ; *** VD_IOPOST -- Called by VMS I/O completion logic by virtue of IRP$L_PID ; containing the system virtual address of VD_IOPOST. This routine ; is only called for segmented virtual I/O to insure that the context ; of the request is restored to use the virtual disk's ACP/XQP ; caches in mapping virtual blocks to logical blocks and call ; VD_STARTIO to map the virtual disk LBNs to the physical disk LBNs. ; ; The "context" of the orignal I/O request is saved in a "pending ; I/O identification block" (PIB) linked to the local PENDQFL ; queue list head. The critical values which must be ; saved in the PIB are the contents of the IRP$L_PID field ; and the virtual disk's UCB address. A back pointer to the IRP ; is saved in the PIB to uniquely identify the IRP associated ; with each PIB. ; ; *** ; ; On Entry: ; ; R5 = IRP address ; IPL = IPL$_IOPOST ; VD_IOPOST:: MOVAL PENDQFL,R0 ; point to pending queue list head MOVL R0,R1 ; copy list head address 2$: MOVL (R0),R0 ; step to next PIB CMPL R0,R1 ; at end-of-list ?? BNEQ 6$ ; branch if not, check PIB ;* BUG_CHECK ; else, fatal error...crash system HALT 6$: CMPL PIB$L_IRP(R0),R5 ; PIB linked to IRP ?? BNEQ 2$ ; branch if not, check again ; REMQUE (R0),R0 ; remove PIB from pending queue .IIF DEFINED DEBUG, JSB G^INI$BRK MOVL IRP$L_UCB(R5),R1 ; pickup host disk UCB address DECW UCB$W_QLEN(R1) ; take care of things normally done in ; IOC$IOPOST BGEQ 14$ ; branch if okay CLRW UCB$W_QLEN(R1) ; force queue length to 0 14$: MOVL PIB$L_UCB(R0),IRP$L_UCB(R5) ; restore VDDRIVER's UCB address MOVL PIB$L_PID(R0),IRP$L_PID(R5) ; restore PID in IRP and... INSQUE (R5),G^IOC$GL_PSFL ; put IRP back in post-proc queue INSQUE (R0),FREEQFL ; insert PIB on free queue RSB ; and return to IOC$IOPOST ; VD_END:: .END