From:	CRDGW2::CRDGW2::MRGATE::"SMTP::CRVAX.SRI.COM::RELAY-INFO-VAX" 22-NOV-1990 11:06:48.59
To:	MRGATE::"ARISIA::EVERHART"
CC:	
Subj:	The RIGHT version of RAMDRIVER, part 2 of 5

Received:  by crdgw1.ge.com (5.57/GE 1.78)
	 id AA26527; Thu, 22 Nov 90 10:47:13 EST
Received: From UCBVAX.BERKELEY.EDU by CRVAX.SRI.COM with TCP; Thu, 22 NOV 90 04:48:05 PST
Received: by ucbvax.Berkeley.EDU (5.63/1.42)
	id AA28408; Thu, 22 Nov 90 04:35:13 -0800
Received: from USENET by ucbvax.Berkeley.EDU with netnews
	for info-vax@kl.sri.com (info-vax@kl.sri.com)
	(contact usenet@ucbvax.Berkeley.EDU if you have questions)
Date: 21 Nov 90 00:37:18 GMT
From: gkn@ucsd.edu  (Gerard K. Newman)
Organization: San Diego Supercomputer Center
Subject: The RIGHT version of RAMDRIVER, part 2 of 5
Message-Id: <23373@ucsd.Edu>
Sender: info-vax-request@kl.sri.com
To: info-vax@kl.sri.com


-+-+-+-+-+-+-+-+ START OF PART 2 -+-+-+-+-+-+-+-+
X; ----- RAMD_UNLOAD:  Driver unload routine
X;
X;
X; This routine is called in response to the SYSGEN UNLOAD command.
X;
X; This driver is not unloadable.
X;
X; Environment:
X;
X;`009IPL$_POWER, process context.
X;
X; Inputs:
X;
X;`009None.
X;
X; Outputs:
X;
X;`009None.
X;
X;-
X
XRAMD_UNLOAD:`009`009`009`009;Driver unload routine
X
X`009RSB`009`009`009`009;Driver is not unloadable.
X
X`009.Page
X`009.Subtitle`009RAMD_CTRL_INIT`009- Controller initialization
X
X;+
X;
X; ----- RAMD_CTRL_INIT:  Controller initialization
X;
X;
X; This routine is called to perform controller specific initialization
X; during the SYSGEN LOAD or RELOAD  commands and  during power failure
X; recovery.  Since  there isn't a  controller this  routine doesn't do
X; a whole lot.
X;
X; Environment:
X;
X;`009IPL$_POWER, system context.
X;
X; Inputs:
X;
X;`009R5`009- IDB address
X;
X; Outputs:
X;
X;`009Not much.
X;
X;-
X
XRAMD_CTRL_INIT:`009`009`009`009;Controller initialization
X
X;%%%`009JSB`009G`094INI$BRK`009`009;%%% Whistle up XDELTA
X`009CLRL`009IDB$L_CSR(R5)`009`009;No CSR
X`009RSB`009`009`009`009;Done
X
X`009.Page
X`009.Subtitle`009RAMD_UNIT_INIT`009- Unit initialization
X
X;+
X;
X; ----- RAMD_UNIT_INIT:  Unit initialization
X;
X;
X; This routine is called during the SYSGEN LOAD command (but not RELOAD) and
X; during power failure recovery.  We initialize our MUTEX and return.
X;
X; Environment:
X;
X;`009IPL$_POWER, system context.
X;
X; Inputs:
X;
X;`009R5`009- UCB address
X;
X; Outputs:
X;
X;`009Unit set online.
X;
X;-
X
XRAMD_UNIT_INIT:`009`009`009`009`009;Unit initialization
X
X`009BBSC`009#UCB$V_POWER,UCB$L_STS(R5),10$`009;Nothing to do on powerfail re
Vcovery
X`009MOVL`009#`094xFFFF,UCB$L_RAMD_MUTEX(R5)`009;Initialize the MUTEX
X`009BISW`009#UCB$M_ONLINE,UCB$W_STS(R5)`009;We're on the air
X
X10$:`009RSB`009`009`009`009`009;Done.
X
X`009.Page
X`009.Subtitle`009RAMD_PACKACK`009- Handle IO$_PACKACK
X
X;+
X;
X; ----- RAMD_PACKACK:  Handle IO$_PACKACK
X;
X;
X; This routine is called during FDT processing to handle IO$_PACKACK.
X; We set the valid bits in the UCB and return.
X;
X; Environment:
X;
X;`009IPL$_ASTDEL, process context.
X;
X; Inputs:
X;
X;`009R5`009- UCB address
X;`009R3`009- IRP address
X;
X; Outputs:
X;
X;`009Volume valid changed.
X;
X;-
X
XRAMD_PACKACK:`009`009`009`009;Handle IO$_PACKACK
X
X`009BBSS`009#UCB$V_LCL_VALID,UCB$L_STS(R5),10$ ;Already valid?
X`009BISW`009#UCB$M_VALID,UCB$L_STS(R5)`009   ;No, it is now.
X`009INCB`009UCB$B_ONLCNT(R5)`009`009   ;Another system has this one on line
X
X10$:`009MOVL`009#SS$_NORMAL,R0`009`009;Success!
X`009JMP`009G`094EXE$FINISHIOC`009`009;Post the I/O request complete
X
X`009.Page
X`009.Subtitle`009RAMD_UNL_AVL`009- Handle IO$_UNLOAD and IO$_AVAILABLE
X
X;+
X;
X; ----- RAMD_UNL_AVL:  Handle IO$_UNLOAD and IO$_AVAILABLE
X;
X;
X; This routine is called as an FDT routine for IO$_UNLOAD and IO$_AVAILABLE.
X; We clear the volume valid bits and post the I/O request complete.
X;
X; Environment:
X;
X;`009IPL$_ASTDEL, process context.
X;
X; Inputs:
X;
X;`009R5`009- UCB address
X;`009R3`009- IRP address
X;
X; Outputs:
X;
X;`009Volume valid cleared.
X;
X;-
X
XRAMD_UNL_AVL:`009`009`009`009;Handle IO$_UNLOAD and IO$_AVAILABLE
X
X`009BBCC`009#UCB$V_LCL_VALID,UCB$L_STS(R5),10$ ;Volume valid?
X`009BICW`009#UCB$M_VALID,UCB$W_STS(R5)`009   ;Volume no longer valid
X`009DECB`009UCB$B_ONLCNT(R5)`009`009   ;Another system has let go of this on
Ve
X
X10$:`009MOVL`009#SS$_NORMAL,R0`009`009;Success!
X`009JMP`009G`094EXE$FINISHIOC`009`009;Post the I/O request complete.
X
X`009.Page
X`009.Subtitle`009RAMD_FORMAT`009- Handle IO$_FORMAT
X
X;+
X;
X; ----- RAMD_FORMAT:  Handle IO$_FORMAT
X;
X;
X; This routine is called as an FDT routine for IO$_FORMAT, which is
X; used to allocate and deallocate nonpaged pool which comprises the
X; blocks of  the "disk".  P1 is the new size of the disk.  We first
X; deallocate the  existing chunk (all data is lost), and allocate a
X; chunk of the new size.
X;
X; Environment:
X;
X;`009IPL$_ASTDEL, process context.
X;
X; Inputs:
X;
X;`009AP`009- Address of P1
X;`009R5`009- UCB address
X;`009R3`009- IRP address
X;
X; Outputs:
X;
X;`009Disk size changed.
X;
X;-
X
XRAMD_FORMAT:`009`009`009`009;Handle IO$_FORMAT
X
X`009PUSHL`009R3`009`009`009;Keep the IRP safe
X
X; Lock the disk MUTEX for write access.
X
X`009MOVAB`009UCB$L_RAMD_MUTEX(R5),R0`009;Address the MUTEX
X`009JSB`009G`094SCH$LOCKW`009`009;Lock the MUTEX for write access.
X
X; Ensure that the disk isn't mounted.
X
X`009MOVL`009#SS$_DEVMOUNT,R0`009`009 ;Presume failure
X`009BBS`009#DEV$V_MNT,UCB$L_DEVCHAR(R5),40$ ;Branch if it's mounted.
X
X; Next deallocate the existing buffer.
X
X`009MOVL`009UCB$L_RAMD_BUFF(R5),R0`009   ;Grab the existing buffer
X`009BEQL`00920$`009`009`009   ;None there, that was easy.
X`009MULL3`009#512,UCB$L_MAXBLOCK(R5),R1 ;Grab the size to deallocate
X`009BEQL`00910$`009`009`009   ;Eh?
X`009JSB`009G`094EXE$DEANONPGDSIZ`009   ;Deallocate the block.
X
X10$:`009CLRL`009UCB$L_RAMD_BUFF(R5)`009   ;No more buffer.
X
X; Allocate a new buffer.
X
X20$:`009MOVL`009#SS$_NORMAL,R0`009`009  ;Presume no new disk to allocate
X`009MOVL`009P1(AP),UCB$L_MAXBLOCK(R5) ;New size to allocate
X`009BEQL`00940$`009`009`009  ;If EQL that was easy
X`009MULL3`009#512,P1(AP),R1`009`009  ;Here's the size we want
X`009JSB`009G`094EXE$ALONPAGVAR`009  ;Allocate some pool
X`009BLBS`009R0,30$`009`009`009  ;Win!
X`009MOVZWL`009#SS$_INSFMEM,R0`009`009  ;Lose.
X`009CLRL`009R2`009`009`009  ;No buffer allocated
X`009CLRL`009UCB$L_MAXBLOCK(R5)`009  ;No blocks, either.
X
X30$:`009MOVL`009R2,UCB$L_RAMD_BUFF(R5)`009  ;Stash the block address
X
X40$:`009MOVW`009UCB$L_MAXBLOCK(R5),UCB$W_CYLINDERS(R5)`009;Cylinders = block
Vs
X`009PUSHL`009R0`009`009`009`009`009;Stash the final status.
X`009MOVAB`009UCB$L_RAMD_MUTEX(R5),R0`009`009`009;Address our MUTEX
X`009JSB`009G`094SCH$UNLOCK`009`009`009`009;Unlock same.
X`009POPR`009#`094m<R0,R3>`009`009`009`009;Restore the status & IRP
X`009JMP`009G`094EXE$FINISHIOC`009`009`009`009;Done
X
X`009.Page
X`009.Subtitle`009RAMD_STARTIO`009- Start I/O routine
X
X;+
X;
X; ----- RAMD_STARTIO:  Start I/O routine
X;
X;
X; This  routine is called  as a fork process to start an I/O request.
X; The only requests which get here are reads and writes (both logical
X; and physical).
X;
X; Recall  that we're  at IPL$_SYNCH and  do not have process context.
X; The  only way we  can move  data to  or from user space is to use a
X; system  virtual  page to  double  map the  user's  buffer  and call
X; IOC$MOVxUSER,  which is very slow and is doubly bad because it must
X; execute at  IPL$_SYNCH.  This  is what  DEC's PDDRIVER does (and is
X; why that driver may not be a win on a busy system).
X;
X; A better solution is  to execute with process context, where we can
X; run at IPL$_ASTDEL and use a MOVC3 instruction to transfer the data
X; to user space.  Normally one would do this sort of thing in the FDT
X; routines and  simply bypass the driver start I/O routine.  However,
X; due to  the complex nature  of the FDT processing required for file
X; oriented devices it is desirable to let DEC's routines do it. Those
X; routines  (in SYSACPFDT) exit  by queueing the  IRP to the driver's
X; start I/O routine, so we're almost (but not quite) stuck.
X;
X; So, what we must do is to get back our lost process context.  We do
X; this by  queueing a kernel mode AST back to the requesting process.
X; The AST  routine (below) will transfer  the data  and then post the
X; I/O request complete.  Once we have our process context back we can
X; backtrack from the  SVAPTE in the IRP to a process virtual address,
X; and can therefore use a MOVC3 for data transfer.
X;
X; We make a special case  out of erase requests (IRPs with IO$V_ERASE
X; set in IRP$W_FUNC).  The normal case for these kinds of requests is
X; that the erase pattern is 0 , and VMS will use the preallocated PPT
X; and erase pattern found at EXE$GL_ERASEPPT and EXE$GL_ERASEPB.  The
X; The  other case would be  that the user  specifies a non-zero erase
X; pattern (probably via  $ERAPAT and setting LOADERAPAT to 1) and the
X; PPT and EPB  are allocated from  non-paged  pool someplace.  In any
X; event, we ignore all of this addres space jiggery-pokerey (although
X; it's pretty  neat, if you think about  it)  and simply  use a MOVC5
X; instruction to process erase requests, using an erase pattern of 0.
X; If someone at the NCSC has a  problem with this then THEY can write
X; the code to deal with it.
X;
X; Another special case is for page reads:  if we're satisfying a page
X; fault from our ramdisk,  then attempting to use the virtual address
X; which caused the  fault to begin  with to write into isn't going to
X; do us  a whole lot of  good.  So, if  we detect  that we're doing a
X; page fault read then we double-map the buffer into system space and
X; use this  address range for the I/O.  We  do it this way because it
X; eases the  synchronization problems with the  swapper  which  would
X; arise if we directly  manipulated the user's page table entries (we
X; would have to  light the valid  bit and KW  and run  owning the MMG
X; interlock at IPL$_SYNCH).  The down side to this is that we place a
X; strain on  free system address space  (SPTREQ) and can cause a fair
X; number of TB flushes,  which aren't too cheap on an SMP system.  We
X; must also  deal with the  case of  satisfying page  fault reads for
X; procsses executing at IPL$_ASTDEL, which of course will prevent the
X; delivery of our AST.  In this case, since we're "never busy", we're
X; guaranteed to still  have current process  context  (necessary  for
X; locking the MUTEX) and  can just skip  the queueing of the AST (but
X; with the performance hit of having to run at IPL$_SYNCH -- too hard
X; to lower IPL back to IPL$_ASTDEL in a controlled fashion).
X;
X; Since we us a MUTEX for synchronization, we can allow multiple read
X; requests to execute in parallel (since we can lock a MUTEX for read
X; or write access).  In order for this to happen the device must look
X; like it is not busy to IOC$INITIATE when it's called to start up an
X; I/O request.  We do this by clearing the busy bit in the UCB before
X; we queue the AST.
X;
X; Environment:
X;
X;`009IPL$_SYNCH, fork context.
X;
X; Inputs:
X;
X;`009R5`009- UCB address
X;`009R3`009- IRP address
X;
X; Outputs:
X;
X;`009IRP used as an ACB and a kernel mode AST is queued to the
X;`009requesting process to complete the I/O request.
X;
X;-
X
XRAMD_STARTIO:`009`009`009`009`009 ;Start I/O routine
X
X`009BICW`009#UCB$M_BSY,UCB$W_STS(R5)`009 ;Device must not look busy
X`009MOVL`009IRP$L_WIND(R3),IRP$L_WCBSAVE(R3) ;Keep the WCB address safe
X`009BITW`009#<IRP$M_PAGIO!IRP$M_SWAPIO>,-`009 ;Paging or
X`009`009IRP$W_STS(R3)`009`009`009 ; swapping I/O?
X`009BNEQ`00910$`009`009`009`009 ;If NEQ yes
X`009MOVL`009R3,R5`009`009`009`009 ;Copy the IRP address and make believe it'
Vs an ACB
X`009BISB`009#ACB$M_KAST,ACB$B_RMOD(R5)`009 ;Say we're a kernel AST
X`009MOVAB`009RAMD_KAST,ACB$L_KAST(R5)`009 ;Execute this routine
X`009CLRL`009R2`009`009`009`009 ;No priority increment (could use PRI$_IOCOM,
V I guess)
X`009JMP`009G`094SCH$QAST`009`009`009 ;Queue the AST and return
X
X; Paging I/O (probably a page fault read).
X;
X; Pathological case:  if the  process  we're doing  paging I/O on  behalf of
V is
X; executing at IPL$_ASTDEL,  then  queuing an AST will  not work  (it'll han
Vg).
X; Since this device doesn't look  busy,  IRPs will never  wind up on our que
Vue,
X; so at  this point we  still have  process context  (i.e.,  we're  not  a f
Vork
X; process)  from the  process which  caused the  page  fault.  So,  in order
V to
X; prevent hangs, skip  the queueing  of the AST  in this case and just drive
V on
X; -- the only thing  to consider here  is that we're  at IPL$_SYNCH rather t
Vhan
X; IPL$_ASTDEL -- some performance impact, but less than hanging the system ;
V-)
X
X10$:`009BSBW`009RAMD_SMAP`009`009;Go map this buffer into system space
X`009BRB`009RAMD_KAST_1`009`009;Join common code
X
X; Ok - we're back at IPL$_ASTDEL with (the correct one, even!) process conte
Vxt.
X; R0-R5 are available for use, and R5 is the address of the IRP (er, ACB).
V  The
X; PCB address is in R4 (thanks to the AST dispatcher).
X
XRAMD_KAST:`009`009`009`009;Ref. label
X
X`009MOVL`009R5,R3`009`009`009;Put the IRP where most everything expects to f
Vind it
X`009MOVL`009IRP$L_UCB(R3),R5`009;Address our UCB
X
XRAMD_KAST_1:`009`009`009`009;Ref. label
X
X; Backtrack from the SVAPTE to a usable virtual address.
X
X`009BSBW`009SVAPTE_TO_VA`009`009`009;Get a usable virtual address
X`009BISW`009IRP$W_BOFF(R3),R2`009`009;Merge in the proper byte offset
X
X; Compute the "disk" address based on whether it's logical or physical I/O.
X
X`009MULL3`009#512,IRP$L_MEDIA(R3),R1`009`009;Presume logical I/O
X`009BBC`009#IRP$V_PHYSIO,IRP$W_STS(R3),10$`009;Branch if it's logical I/O
X
X; Physical I/O ... Byte offset = Sector size * ((Sectors per track) * Track
V + Sector)
X
X`009MOVZBL`009UCB$B_SECTORS(R5),R1`009;Grab the sectors/track
X`009MULW`009IRP$L_MEDIA+2(R3),R1`009;Multiply by track number
X`009ADDW`009IRP$L_MEDIA(R3),R1`009;Add in the sector
X`009DECL`009R1`009`009`009;Make it zero based
X`009MULL`009#512,R1`009`009`009;Multiply by the sector size
X
X; Ok - now we have the byte offset into the disk buffer in R1.
X
X10$:`009MOVAB`009UCB$L_RAMD_MUTEX(R5),R0`009;Address the MUTEX
X`009ADDL`009UCB$L_RAMD_BUFF(R5),R1`009;Point to the right spot in the disk b
Vuffer
X`009PUSHR`009#`094m<R3,R4,R5>`009`009;Save volatile registers
X
X; Dispatch to the the correct routine.
X
X`009MOVAB`009RAMD_READ,R5`009`009`009;Presume it's a read
X`009BBS`009#IRP$V_FUNC,IRP$W_STS(R3),20$`009;Branch if this is the case
X`009MOVAB`009RAMD_WRITE,R5`009`009`009;Else it's a write
X
X20$:`009JSB`009(R5)`009`009`009`009;Call the correct routine.
X
X; Here to post the I/O request complete.
X
X`009POPR`009#`094m<R3,R4,R5>`009`009`009 ;Restore the IRP, PCB, and UCB addr
Vesses
X`009PUSHL`009R3`009`009`009`009 ;Keep the IRP address safe from RAMD_UNLOCK
X`009BSBW`009RAMD_UNLOCK`009`009`009 ;Unlock our MUTEX
X`009POPL`009R3`009`009`009`009 ;Restore the IRP address
X`009MOVL`009IRP$L_WCBSAVE(R3),IRP$L_WIND(R3) ;Restore the window pointer
X`009ASHL`009#16,IRP$L_BCNT(R3),R0`009`009 ;Shift the byte count to the high
V word of R0
+-+-+-+-+-+-+-+-  END  OF PART 2 +-+-+-+-+-+-+-+-
-- 
gkn	Gerard K. Newman		gkn@sds.sdsc.edu	619.534.5076
	San Diego Supercomputer Center	gkn@sdsc.bitnet		619.534.5152 FAX
	PO Box 85608			sdsc::gkn (27.1/span)
	San Diego, CA  92186-9784	ucsd!gkn

