Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_SRC_3_19910112 - monitor/enqsrv.mac
There are 13 other files named enqsrv.mac in the archive. Click here to see a list.
; Edit= 8904 to ENQSRV.MAC on 15-Aug-88 by LOMARTIRE
;Improve BUG. documentation 
; Edit= 8848 to ENQSRV.MAC on 25-May-88 by LOMARTIRE (TCO 7.1292)
;More of edit 8847 - fix all places that quit after a "No" reply 
; Edit= 8847 to ENQSRV.MAC on 24-May-88 by LOMARTIRE (TCO 7.1292)
;Prevent jobs hung in EVWAIT - make all nodes reply to vote 
; UPD ID= 8513, RIP:<7.MONITOR>ENQSRV.MAC.14,   9-Feb-88 15:20:28 by GSCOTT
;TCO 7.1218 - Update copyright date.
; UPD ID= 8460, RIP:<7.MONITOR>ENQSRV.MAC.13,   5-Feb-88 09:34:31 by GSCOTT
;TCO 7.1210 - Set EQNOTF normally not dumpable.
; UPD ID= 8343, RIP:<7.MONITOR>ENQSRV.MAC.12,  15-Jan-88 15:43:39 by LOMARTIRE
;More of TCO 7.1179 - Do not check EN.CLL in EQRSTS.  Always vote if the
;  process has cluster-wide capabilities even if the block does not have
;  EN.CLL set.
; UPD ID= 8341, RIP:<7.MONITOR>ENQSRV.MAC.11,  14-Jan-88 16:04:20 by LOMARTIRE
;TCO 7.1179 - Check EN.CLL before returning success in LOCLOK.  Mixed mode 
;  non-file object locks should only be "found" if this system believes
;  that it is a cluster-wide lock.  This issue is of no concern for file
;  locks since it is already handled via the ENQ Token which makes mixed 
;  mode file lock illegal.  Also, add a check of EN.CLL in routines 
;  EQRSTS, EQPOOL, and EQLKSD.
; UPD ID= 8335, RIP:<7.MONITOR>ENQSRV.MAC.10,   6-Jan-88 13:59:53 by LOMARTIRE
;TCO 7.1172 - Prevent ILLUUO and ILFPTE BUGHLTs on CI-less systems; Move
;  initialization of EQLBLT and EQLBCT from EQSINI into routine ENQINI.
; UPD ID= 308, RIP:<7.MONITOR>ENQSRV.MAC.9,  20-Nov-87 07:11:02 by LOMARTIRE
;TCO 7.1145 - Make the ENQ Answer fork and ENQ Resched fork more responsive
; UPD ID= 305, RIP:<7.MONITOR>ENQSRV.MAC.8,  18-Nov-87 13:16:54 by LOMARTIRE
;TCO 7.1142 - Prevent hung forks by removing race between the setting of 
;  VPRTY on the sending node and the clearing of the left half of EQFKFL
;  on the receiving node.  Make ANSWER retry a send only when it fails
;  due to SCSNEC.  Don't clear the left half of EQFKFL anymore after
;  a cluster state change.
; UPD ID= 300, RIP:<7.MONITOR>ENQSRV.MAC.7,  16-Nov-87 15:13:14 by MCCOLLUM
;TCO 7.1138 - Add routine ENQCST and put the VRPA back here
; UPD ID= 269, RIP:<7.MONITOR>ENQSRV.MAC.6,   6-Nov-87 11:59:08 by LOMARTIRE
;More of TCO 7.1115 - Move VRPA to ENQPAR so CFSSRV can see bit VPRTY
; UPD ID= 262, RIP:<7.MONITOR>ENQSRV.MAC.5,   5-Nov-87 15:43:10 by LOMARTIRE
;TCO 7.1115 - Prevent races between EQAFRK and EQMSG in the updating of the 
;  VRQA by adding the VANA (Vote Answer Area).  All the Vote Responder 
;  routines will use this area as a work area.  The VANA will be copied 
;  from the VRQA by the EQAFRK.  It is the same format as the VRQA.
;  Move the setting of VPRTY from ENQRSV to CFSRSV so cluster state changes
;  are handled correctly.  The clearing of the left half of EQFKFL moves too.
;  Also, add word EQLBCT to count the number of blocks on the EQLBLT.
; UPD ID= 234, RIP:<7.MONITOR>ENQSRV.MAC.4,  29-Oct-87 16:22:59 by LOMARTIRE
;TCO 7.1103 - Make HDRFIP put the user code into the VRQA correctly
; UPD ID= 228, RIP:<7.MONITOR>ENQSRV.MAC.3,  28-Oct-87 14:24:24 by LOMARTIRE
;TCO 7.1096 - Prevent SKDPF1s and PITRAPs by moving ENQRSV processing into 
;  process context.  Also, update ACTION for EQNOTF BUGCHK.
; UPD ID= 161, RIP:<7.MONITOR>ENQSRV.MAC.2,  19-Oct-87 17:12:56 by LOMARTIRE
;TCO 7.1062 - Add support for cluster-wide ENQ

;	COPYRIGHT (c) DIGITAL EQUIPMENT CORPORATION 1976, 1988.
;	ALL RIGHTS RESERVED.
;
;	THIS SOFTWARE IS FURNISHED UNDER A  LICENSE AND MAY BE USED AND  COPIED
;	ONLY IN  ACCORDANCE  WITH  THE  TERMS OF  SUCH  LICENSE  AND  WITH  THE
;	INCLUSION OF THE ABOVE  COPYRIGHT NOTICE.  THIS  SOFTWARE OR ANY  OTHER
;	COPIES THEREOF MAY NOT BE PROVIDED  OR OTHERWISE MADE AVAILABLE TO  ANY
;	OTHER PERSON.  NO  TITLE TO  AND OWNERSHIP  OF THE  SOFTWARE IS  HEREBY
;	TRANSFERRED.
;
;	THE INFORMATION IN THIS  SOFTWARE IS SUBJECT  TO CHANGE WITHOUT  NOTICE
;	AND SHOULD  NOT  BE CONSTRUED  AS  A COMMITMENT  BY  DIGITAL  EQUIPMENT
;	CORPORATION.
;
;	DIGITAL ASSUMES NO  RESPONSIBILITY FOR  THE USE OR  RELIABILITY OF  ITS
;	SOFTWARE ON EQUIPMENT THAT IS NOT SUPPLIED BY DIGITAL.

	SEARCH ENQPAR,SCAPAR,PROLOG
	TTITLE (ENQSRV,,< - Cluster-wide ENQ/DEQ Protocol Server>)

;	David M. Lomartire	20-Oct-87

	EXTN <HSHTBL>		;Defined in STG.MAC

   IFN CLEQIN,<			;Assemble only if cluster-wide code present
	Subttl	Table of Contents

;		     Table of Contents for ENQSRV
;
;				  Section		      Page
;
;
;    1. Data Definitions
;        1.1    The VRQA (Vote Request Area) . . . . . . . . .   4
;        1.2    The VRPA (Vote Reply Area) . . . . . . . . . .   6
;        1.3    The VANA (Vote Answer Area)  . . . . . . . . .   7
;        1.4    Other Variables  . . . . . . . . . . . . . . .   8
;    2. SYSAP Initialization
;        2.1    EQSINI . . . . . . . . . . . . . . . . . . . .   9
;    3. Support Routines
;        3.1    The ENQ Answer fork  . . . . . . . . . . . . .  10
;        3.2    The ENQ Resched fork . . . . . . . . . . . . .  11
;            3.2.1    EQRFWT (Scheduler test)  . . . . . . . .  12
;        3.3    ENQCST (Notify upon Configuration Change)  . .  13
;        3.4    ENQRSV (React to Configuration Change) . . . .  14
;        3.5    EQMSG (Incoming Message) . . . . . . . . . . .  15
;        3.6    LOCLOK (Find the Lock-Block) . . . . . . . . .  17
;        3.7    HDRFIQ (Fill in header from Q-Block) . . . . .  19
;        3.8    HDRFIL (Fill in header from Lock-Block)  . . .  21
;        3.9    HDRFIP (Fill in header from P registers) . . .  23
;    4. Vote Requester
;        4.1    ASK4IT (Send out votes)  . . . . . . . . . . .  25
;            4.1.1    EVWAIT (Scheduler test)  . . . . . . . .  30
;            4.1.2    BUFHDR (Fill in Buffer Header) . . . . .  31
;        4.2    Dispatch Table (EIDSPT)  . . . . . . . . . . .  32
;        4.3    EIEOKR (Reply to .EOKR vote) . . . . . . . . .  33
;        4.4    EICNTS (Reply to .CNTS vote) . . . . . . . . .  34
;        4.5    EIRSTS (Reply to .RSTS vote) . . . . . . . . .  35
;        4.6    EIPOOL (Reply to .POOL vote) . . . . . . . . .  36
;        4.7    EIQSKD (Reply to .QSKD vote) . . . . . . . . .  37
;        4.8    EILKSD (Reply to .LKSD vote) . . . . . . . . .  38
;    5. Vote Responder
;        5.1    Dispatch Table (EVDSPT)  . . . . . . . . . . .  39
;        5.2    ANSWER (Send the Reply)  . . . . . . . . . . .  40
;        5.3    EVEOKR (Respond to .EOKR vote) . . . . . . . .  41
;        5.4    EVCNTS (Respond to .CNTS vote) . . . . . . . .  43
;        5.5    EVRSTS (Respond to .RSTS vote) . . . . . . . .  44
;        5.6    EVPOOL (Respond to .POOL vote) . . . . . . . .  45
;        5.7    EVQSKD (Respond to .QSKD vote) . . . . . . . .  46
;        5.8    EVLKSD (Respond to .LKSD vote) . . . . . . . .  47
;    6. Interface Routines for ENQ
;        6.1    EQEOKR (Check ENQ% request)  . . . . . . . . .  48
;        6.2    EQCNTS (Get lock usage counts) . . . . . . . .  50
;        6.3    EQRSTS (Get remote lock status)  . . . . . . .  51
;        6.4    EQPOOL (Get pool total)  . . . . . . . . . . .  53
;        6.5    EQQSKD (QSKED remote)  . . . . . . . . . . . .  54
;        6.6    EQLKSD (Notify for remote LOKSKD)  . . . . . .  55
;    7. End of ENQSRV  . . . . . . . . . . . . . . . . . . . .  56
	SUBTTL	Data Definitions -- The VRQA (Vote Request Area)

EHDRSZ==^D3			;ENQSRV Header
LDCHDR==^D5			;The constant part of the Lock Descriptor Header
LDMASK==^D15			;The mask block in the Lock Descriptor Header
LDASCI==^D50			;The string in the Lock Descriptor Header
LDTSIZ==LDCHDR+LDMASK+LDASCI	;The total length of the Lock Descriptor Header
ADDATA==^D3			;The number of words of Additional Data
				;Note that 2 more words could be used before 
				;another SCA message buffer (#3) would be 
				;required for a full Request Message Set

VQSIZE==.MHUDA+EHDRSZ+LDTSIZ+ADDATA  ;[7.1115] Size of the VRQA and VANA
RS	VRQA,VQSIZE		;The VRQA

;Word .MHUDA of the SCA message buffer is first word of ENQSRV Header
DEFSTR	EBDOFF,.MHUDA,17,18	;Offset from .MHUDA to .EBSOD
DEFSTR	EBPKTN,.MHUDA,26,9	;The total number of messages in Request Set
DEFSTR	EBPNUM,.MHUDA,35,9	;This message number

.EBFFW==.MHUDA+1		;This must match word SCALEN in CFS vote buffer
DEFSTR	EBFLAG,.EBFFW,11,12	;Flags used by ENQSRV
   DEFSTR EQNOV,.EBFFW,0,1	;No vote required for this lock
   DEFSTR EQNO,.EBFFW,1,1	;Another node said "NO"
   DEFSTR EQANS,.EBFFW,2,1	;This is a reply to a vote request
   DEFSTR EQBLN,.EBFFW,3,1	;Ignore level numbers for this vote
   DEFSTR EQTXT,.EBFFW,4,1	;Lock is described by a text string
DEFSTR	EBCFSC,.EBFFW,17,6	;Function code - .CFENQ - Must match CFCOD
DEFSTR	EBUNIQ,.EBFFW,35,18	;Vote unique code

.EBEOH==.EBFFW+1
DEFSTR	EBNODE,.EBEOH,11,12	;CI node number
DEFSTR	EBFTYP,.EBEOH,17,6	;Function code for ENQSRV
   .EOKR==1			;Vote from EQEOKR Interface routine
   .CNTS==2			;Vote from EQCNTS Interface routine
   .RSTS==3			;Vote from EQRSTS Interface routine
   .POOL==4			;Vote from EQPOOL Interface routine
   .QSKD==5			;Vote from EQQSKD Interface routine
   .LKSD==6			;Vote from EQLKSD Interface routine
DEFSTR	EBTOTT,.EBEOH,35,18	;Total number of users words in this packet
.EBSOD==.EBEOH+1		;The start of the Lock Descriptor Header
DEFSTR	EBQFLG,.EBSOD,11,12	;Flags from the Q or Lock Block
                                ;Bits 12 to 17 are RESERVED
DEFSTR	EBTYPE,.EBSOD,35,18	;ENQOFN word from Lock-Block

.EBFL1==.EBSOD+1
DEFSTR	EBSTRN,.EBFL1,35,36	;The structure name in SIXBIT

.EBFL2==.EBFL1+1
DEFSTR	EBADDR,.EBFL2,35,36	;The index block address

.EBGHV==.EBFL2+1
DEFSTR	EBGRP,.EBGHV,17,18	;The group number
DEFSTR	EBHASH,.EBGHV,35,18	;The hash value for the Lock-Block

.EBWCT==.EBGHV+1
DEFSTR	EBMBWS,.EBWCT,17,18	;The number of words in the mask block
DEFSTR	EBTSWS,.EBWCT,35,18	;The number of words in the ASCIZ string

.EBSMB==.EBWCT+1		;The start of the Mask Block
.EBAD1==.EBSMB			;Also, the start of Additional Data in reply
.EBAD2==.EBAD1+1		;Second word of Additional Data in reply
.EBAD3==.EBAD2+1		;Third word of Additional Data in reply
	SUBTTL	Data Definitions -- The VRPA (Vote Reply Area)

VPSIZE==ADDATA+1		;Size = Additional data plus 1 flag word
RS	VRPA,VPSIZE		;The VRPA

;The first word is in the same format as word .EBFFW of the VRQA.
.VPWD0==0			;Word 0 of the VRPA (first word)
DEFSTR	VPFLAG,.VPWD0,11,12	;Flags used by ENQSRV
   DEFSTR VPNOV,.VPWD0,0,1	;No vote required for this lock
   DEFSTR VPNO,.VPWD0,1,1	;Another node said "NO"
   DEFSTR VPRTY,.VPWD0,2,1	;A cluster state change occurred
                                ;Bits 12 to 17 are RESERVED
DEFSTR	VPUNIQ,.VPWD0,35,18	;Vote unique code

.VPAD1==.VPWD0+1		;Start of Additional Data in reply
.VPAD2==.VPAD1+1 		;Second word of Additional Data in reply
.VPAD3==.VPAD2+1 		;Third word of Additional Data in reply
 	SUBTTL	Data Definitions -- The VANA (Vote Answer Area)

RS	VANA,VQSIZE		;[7.1115] The VANA - same format as VRQA
	SUBTTL	Data Definitions -- Other Variables

;The locations VOTUNI to EQCSTF are actually located in the first 8 words
;[7.1115] of the VRQA.  There are no extra words remaining.
;[7.1115] Note that since the VANA is the same format as the VRQA, it has
;[7.1115] 8 spare words at the top.  Of these, there are 8 remaining.

VOTUNI==VRQA			;The vote unique code
VOTVCT==VOTUNI+1		;The number of outstanding replies
VRBADR==VOTVCT+1		;The address of the vote reply buffer
ASMPTR==VRBADR+1		;Offset into VRQA used by EQMSG
RPLYND==ASMPTR+1		;CFS host index to send reply to
EQLBLT==:RPLYND+1		;Lock-Block action list
EQLBCT==:EQLBLT+1		;[7.1115] Count of blocks on action list
EQCSTF==EQLBCT+1		;[7.1138] Cluster state change flag
	SUBTTL	SYSAP Initialization -- EQSINI

;EQSINI - ENQSRV SYSAP Initializaton
;
;The  main  thing  required of this routine is to obtain the VRB. This
;routine is only called during SYSAP initialization  by  SCA  via  the
;CALL @INITAB in routine SCA::.
;
;Note that the first word of the invisible area in the message buffer is
;zeroed.  This is checked by CFSWDN to determine if the buffer should be
;returned to SCA.  The VRB is never returned after a message send but a
;message buffer obtained to send a Request Message set is.
;
;Returns +1:	Always

	XRESCD

EQSINI::MOVEI T1,1
	CALL <XENT SC.ABF>	;(T1/T1,T2,T3)Get message buffer for VRB
	 BUG.(HLT,EQNVRB,ENQ,SOFT,<ENQ - Could not get buffer for VRB>,<<T1,ERR>>,<

Cause:	SC.ABF was called to acquire a buffer for use as the VRB.  However,
	no buffers were available in the SCA message pool.  We cannot 
	continue without a VRB.

Action:	Examine the dump for signs of damage to the SCA message pool.

Data:	ERR - Error code returned by SCA.
>)
	MOVEM T1,VRBADR		;Save the buffer address
	SETZM -C%BINV(T1)	;No return address (checked in CFSWDN)
	RET
	SUBTTL	Support Routines -- The ENQ Answer fork

 	XSWAPCD

;This is the fork which sends the reply to the received vote request.

EQARUN::MOVX T1,USRCTX		;Start with user context set
	MOVEM T1,FFL
	MCENTR			;Start a new process
	MOVEI T1,JP%SYS+1	;[7.1145] Set higher priority and queue 1
	MOVEM T1,JOBBIT		;[7.1145] Put in PSB
	MOVE T1,FORKX		;Get our fork number
	MOVEM T1,EQAFRK		;Save it away
	MOVEI T1,.ENECL		;Enable this process for ...
	ENQ%			;... cluster-wide ENQ/DEQ
	 NOP			;This call cannot fail
EQALOP:	MOVEI T1,EQAFWT		;The wait test
	MDISMS			;Wait now

EQATOP:	CIOFF			;[7.1115] Prevent state changes
	HLRZ T1,EQFKFL		;Get the unique code of the vote waiting
	SKIPE T1		;[7.1115] Is there one?
	IFSKP.			;[7.1115] No
	  CION			;[7.1115] Turn CI back on
	  JRST EQALOP		;[7.1115] Go wait again
	ENDIF.			;[7.1115] 

;Note:  A restriction here is that this fork cannot get the local ENQ lock
;because the ENQ resched fork can end up locking the local database and then 
;waiting for the cluster-wide lock.  So, in that case, the reply fork would
;not be able to get the local lock and so no reply would be able to be sent.
;This would cause hung forks to occur during cluster state changes.

;	NOINT			;Must be NOINT for lock
;	S1XCT <LOKK ENQLKK>	;Lock up the local ENQ data base

;[7.1115] Now transfer the VRQA into the VANA.  All the Vote Responder 
;[7.1115] routines will work with the VANA.  Should a cluster state 
;[7.1115] change force a revote, then the changing to the VRQA (by either
;[7.1115] EQMSG or the Vote Requester) will not disrupt the VANA or the 
;[7.1115] reply in progress.

	XMOVEI T1,VRQA		;Get the address of the VRQA
	MOVEI T2,.MHUDA(T1)	;[7.1115] This is source of BLT
	LOAD T3,EBMBWS,(T1)	;[7.1115] Get the number of mask block words
	LOAD T1,EBTSWS,(T1)	;[7.1115] Get the number of string words
	ADDI T1,EHDRSZ+LDCHDR+ADDATA(T3)  ;[7.1115] Maximum size of transfer
	XMOVEI T3,VANA		;[7.1115] Destination is start of ...
	ADDI T3,.MHUDA		;[7.1115] ... ENQSRV Header in the VANA
	CALLX (MSEC1,XBLTAT)	;[7.1115] (T1,T2,T3/T1,T2,T3)Transfer VRQA to VANA
	CION			;[7.1115] Now state changes can occur
	XMOVEI T1,VANA		;[7.1115] Get address of VANA
	LOAD T1,EBFTYP,(T1)	;Get the type of ENQ function
	CALL @EVDSPT-1(T1)	;() Reply to the Request Message Set
;	S1XCT <UNLOKK ENQLKK>	;Free up the local data base
;	OKINT
	JRST EQATOP		;See if it was succesfully done

 	SUBTTL	Support Routines -- The ENQ Answer fork -- EQAFWT (Scheduler test)

	RESCD

EQAFWT:	HLRZ T1,EQFKFL		;Get the unique code of vote to reply to
	SKIPN T1		;Is there one waiting?
	JRST 0(T4)		;No so go back to sleep
	JRST 1(T4)		;Yes, so send the reply
	SUBTTL	Support Routines -- The ENQ Resched fork

 	XSWAPCD

;This is the fork which handles a cluster state change.

EQRRUN::MOVX T1,USRCTX		;Start with user context set
	MOVEM T1,FFL
	MCENTR			;Start a new process
	MOVEI T1,JP%SYS+1	;[7.1145] Set higher priority and queue 1
	MOVEM T1,JOBBIT		;[7.1145] Put in PSB
	MOVE T1,FORKX		;Get our fork number
	MOVEM T1,EQRFRK		;Save it away
	MOVEI T1,.ENECL		;Enable this process for ...
	ENQ%			;... cluster-wide ENQ/DEQ
	 NOP			;This call cannot fail
EQRLOP:	MOVEI T1,EQRFWT		;The wait test
	MDISMS			;Wait now

EQRTOP:	HRRZ T1,EQFKFL		;Are there any locks to be rescheduled?
	SKIPL EQCSTF		;[7.1096] Or a cluster state change occurred?
    	JUMPE T1,EQRLOP		;No, so just go to sleep again
	NOINT			;Must be NOINT for lock
	S1XCT <LOKK ENQLKK>	;Lock up the local ENQ data base
	CALLX (MSEC1,CFEQLK)	;()Now lock database cluster-wide
	SETZ T1,		;[7.1096]
	EXCH T1,EQCSTF		;[7.1096] Get flag and reset to zero
	SKIPE T1		;[7.1096] Did cluster change state?
	CALL ENQRSV		;[7.1096] ()Yes, so mark all locks for resched
	MOVE T1,EQLBLT		;Get the first block on the action list
	DO.
	  CAIN T1,-1		;All done with the list?
	  EXIT.			;Yes, all done
	  LOAD T2,ENQFLG,(T1)	;Get the flags from the Lock-Block
	  TXZN T2,EN.SDO	;Is this one slated for resched?
	  IFSKP.
	    STOR T2,ENQFLG,(T1)	;Clear the resched bit
	    CALL REESKD		;(T1/T1)Resched this lock
	    SOS EQFKFL		;One less lock to reschedule
	  ENDIF.
	  LOAD T1,ENQAFP,(T1)	;Get the next block on the list
	  LOOP.			;Continue search
	ENDDO.
	CALLX (MSEC1,CFEQUL)	;()Unlock database cluster-wide
	S1XCT <UNLOKK ENQLKK>	;Free up the local data base
	OKINT
	JRST EQRTOP		;Do all checks again
	SUBTTL	Support Routines -- The ENQ Resched fork -- EQRFWT (Scheduler test)

	RESCD

EQRFWT:	HRRZ T1,EQFKFL		;Get the number of Lock-Blocks to reschedule
	SKIPE T1		;[7.1096] Are there any?
	JRST 1(T4)		;[7.1096] Yes, so reschedule them
	SKIPN EQCSTF		;[7.1096] Did cluster change state?
	JRST 0(T4)		;No so go back to sleep
	JRST 1(T4)		;Yes, so reschedule all locks
	SUBTTL	Support Routines -- ENQCST (Notify upon Configuration Change)

;[7.1138]  ENQCST - Notify upon Cluster State Change
;
;This routine is called by CFSRSV to notify ENQSRV of a cluster state change.
;Various bits and locations are set such that the ENQ Resched fork will 
;awaken and restart all queued Lock-Blocks.  Also, the VPRTY bit is set so 
;that any vote which is waiting for replies will be restarted.
;
;	CALL ENQCST
;
;Returns +1:	Always and trashes no ACs

;	RESCD

ENQCST::SAVEAC <T1>		;Do not trash T1
	XMOVEI T1,VRPA		;Get the VRPA address
	SETONE VPRTY,(T1)	;Vote in progress should be restarted
	SETOM EQCSTF		;Notify ENQSRV of cluster state change
	RET			;All done
	SUBTTL	Support Routines -- ENQRSV (React to Configuration Change)

;ENQRSV - React to Cluster Configuration Change
;
;This  routine  is responsible for handling cluster state transitions.
;Every blocked or queued request for a lock is marked for resched.
;
;This  routine expects to be called from process context with both the
;local and cluster-wide ENQ Database locks
;
;	CALL ENQRSV
;
;Returns +1:	Always
;
;Upon return, every queued or blocked request (on list EQLBLT) will be
;marked  for  a  vote retry by setting bit EN.SDO. The current vote in
;progress is marked for retry by setting VPRTY in the VRPA.  Note that 
;this was done prior to calling this routine by ENQCST.

 	XSWAPCD			;[7.1096]

ENQRSV:	MOVE T1,EQLBLT		;[7.1115] Get the first block on action list
	DO.
	  CAIN T1,-1		;All done with the list?
	  EXIT.			;Yes, all done
	  LOAD T2,ENQFLG,(T1)	;Get the flags from the Lock-Block
	  TXON T2,EN.SDO	;Mark that it should be rescheduled
	  AOS EQFKFL		;Count this only if bit not already set
	  STOR T2,ENQFLG,(T1)	;Replace the flags
	  LOAD T1,ENQAFP,(T1)	;Get the next block on the list
	  LOOP.			;Continue search
	ENDDO.
	RET			;Done
	SUBTTL	Support Routines -- EQMSG (Incoming Message)

;EQMSG - Handle an incoming SCA message
;
;This  routine  is  the general message handler. It is responsible for
;handling  incoming  vote  requests  and  incoming  replies  to   vote
;requests.  It  is  called  from  SCMSG  (in  CFSSRV)  when the .SSMGR
;callback occurs.
;
;	 Q1,T1/ Address of incoming message buffer
;	    T2/ CFCOD = EBCFSC = .CFENQ
;.MHSCI(Q1),T3/ SID index into CFS tables
;.MHDCI(Q1),T4/ Buffer return address
;
;This routine expects to be called at CI interrupt level independently
;of  SCAILK. It assumes that all replies to vote requests will require
;only a single SCA message buffer.
;
;Returns +1:	Always, to SCMSG; Preserves T1.

 	XRESCD			;[7.1096]

EQMSG::	SAVEQ			;Get some work registers
	TMNN EQANS,(Q1)		;Is this a reply to a vote?
	IFSKP.			;Yes
	  LOAD T3,EBUNIQ,(Q1)	;Get the unique code
	  CAME T3,VOTUNI	;Does it match the vote in progress?
	  JRST EQMDON		;No, so ignore this message
	  SOS VOTVCT		;One less node to wait for
	  LOAD T3,EBFTYP,(Q1)	;Get the type of ENQ function
	  CALL @EIDSPT-1(T3)	;(Q1/) Process the reply
	  JRST EQMDON		;Done with the reply
	ENDIF.

;This is a Request Message Set packet - an incoming vote request.
;If it is the first of the Set, then the constant header portion is transferred
;into the VRQA.  The rest of the packet from offset .EBSMB onward is then
;assembled into the VRQA for this and every subsequent packet for the Set.

	XMOVEI Q2,VRQA		;Get the address of the VRQA
	LOAD T2,EBPNUM,(Q1)	;Get message number
  	CAIE T2,1		;Is this the first of the set?
	IFSKP.			;Yes
	  MOVEI T1,EHDRSZ+LDCHDR  ;Size of constant header portion of VRQA
	  MOVE T2,Q1		;Get start of buffer
	  ADDI T2,.MHUDA	;Offset to ENQSRV Header portion
	  MOVEI T3,.MHUDA(Q2)	;Beginning of ENQSRV Header in VRQA
	  CALLX (MSEC1,XBLTAT)	;(T1,T2,T3/T1,T2,T3)Copy constant header to VRQA
	  HRRM T3,ASMPTR	;Initialize offset into VRQA for transfer
	  SETZRO EBTSWS,(Q2)	;Clear out count of string length
	ENDIF.
	LOAD T1,EBTOTT,(Q1)	;Get the total number of user words for buffer
	SUBI T1,EHDRSZ+LDCHDR	;Constant header is included in EBTOTT - remove
	MOVE T2,Q1		;Get start of buffer
	ADDI T2,.EBSMB		;Offset to non-constant portion in buffer
	MOVE T3,ASMPTR		;Get destination offset into VRQA
	CALLX (MSEC1,XBLTAT)	;(T1,T2,T3/T1,T2,T3)Non-constant part to VRQA
	HRRM T3,ASMPTR		;Update the assembly offset
	LOAD T2,EBTSWS,(Q1)	;Get string length in buffer
	LOAD T3,EBTSWS,(Q2)	;Get current count in VRQA
	ADD T3,T2		;Update to reflect new amount
	STOR T3,EBTSWS,(Q2)	;Restore in VRQA
	LOAD T3,EBPKTN,(Q1)	;Get the total number of packets for vote
	LOAD T2,EBPNUM,(Q1)	;Get this message number 
	CAME T3,T2		;Is this the last buffer
	JRST EQMDON		;No, so quit now

;This is the last packet for a Request Message Set.  Set up various values in 
;the VRQA in preparation for the reply and dispatch to the appropriate Vote
;Responder routine to respond to the vote request.

	MOVE T1,MYPOR4		;Get our node number
	STOR T1,EBNODE,(Q2)	;Put in VRQA for reply
	SETONE EQANS,(Q2)	;Indicate that this is a reply message
	MOVEI T1,1
	STOR T1,EBPKTN,(Q2)	;Replies are only a single packet
	STOR T1,EBPNUM,(Q2)
	MOVEI T1,EHDRSZ+LDCHDR	;Setup length as constant header only for now
	STOR T1,EBTOTT,(Q2)	;It will be INCRed as Additional Data is added
	MOVE T1,.MHSCI(Q1)	;Get host index
	MOVEM T1,RPLYND		;Save this for later use by ANSWER
	LOAD T1,EBUNIQ,(Q2)	;Get the unique code of this vote
	HRLM T1,EQFKFL		;Save this in the ENQ fork flag ...
				;... This will cause the ENQ fork to wake up

EQMDON:	MOVE T1,Q1		;Get buffer address to return
	RETSKP			;Return to SCMSG - buffer is returned to port
	SUBTTL	Support Routines -- LOCLOK (Find the Lock-Block)

;LOCLOK - Routine used to find the Lock-Block described in the VANA.
;
;	The VANA contains the reassembled Request Message Set.
;
;Returns +1:	No Lock-Block found
;	 +2:	Lock-Block found with address in T1

 	XSWAPCD

LOCLOK:	SAVEQ			;Save some work registers
	STKVAR <FNDLKI>
	SETZM Q3		;Assume no Lock-Block found
	XMOVEI Q1,VANA		;[7.1115] Get address of VANA
	LOAD Q2,EBHASH,(Q1)	;Get hash index for Lock-Block
	ADD Q2,[HSHTBL]		;Index into the table
	MOVEM Q2,FNDLKI		;Save as initial index
LOCLOP:	LOAD T1,ENQNHC,(Q2)	;Get next entry in hash table
	CAMN T1,FNDLKI		;Back to the hash table
	RET			;Yes, so search is done
	MOVEM T1,Q2		;Save as next block to step from
	LOAD T2,ENQOFN,(T1)	;Get the lock type from the Lock-Block
	LOAD T3,EBTYPE,(Q1)	;Get the lock type from the VRQA
	HRRES T2		;Extend the sign of both
	HRRES T3
	SKIPGE T3		;Is vote an OFN or a non-file lock?
	IFSKP.			;An OFN
	  SKIPGE T2		;Is this lock an OFN too?
	  JRST LOCLOP		;No so step to the next one
	  LOAD T3,STRX,(T2)	;Get the structure number
	  MOVE T3,STRTAB(T3)	;Get SDB address
	  SKIPE SDBALS(T3)	;Is there an alias for this one?
	  SKIPA T3,SDBALS(T3)	;Yes, so use it
	  MOVE T3,SDBNAM(T3)	;No, so use physical name
	  CAME T3,.EBFL1(Q1)	;Does it match what is in the VRQA?
	  JRST LOCLOP		;No so step to next one
	  LOAD T3,STGADR,SPTH(T2)  ;Get index block address
	  LOAD T2,EBADDR,(Q1)	;Get it from the VRQA too
	ENDIF.
	CAME T2,T3		;Are they the same?
	JRST LOCLOP		;No, so step to next 
	LOAD T2,EBMBWS,(Q1)	;Get length of mask block
	ADDI T2,.MHUDA+EHDRSZ+LDCHDR  ;Add constant header amount
	ADD T2,Q1		;Point to start of string in VRQA
	TMNE EQTXT,(Q1)		;Does VRQA have a text string?
	IFSKP.			;No
	  MOVE T2,(T2)		;So actually get the user code
	ELSE.			;Yes this is a text string
	  HRLI T2,(POINT 7,0)	;So make this a byte pointer to the string
	ENDIF.
	CALL STVCMP		;(T1,T2/)Compare the block to the VRQA
	 JRST LOCLOP		;Not found - keep looking
	MOVE T1,Q2		;Found it!  Get address of block
	LOAD T2,ENQFLG,(Q2)	;[7.1179] Get Lock-Block flags
	TXZ T2,EN.NOV		;[7.1179] We must always vote on this lock
	STOR T2,ENQFLG,(Q2)	;[7.1179] Put back the flags
	TXNN T2,EN.CLL		;[7.1179] Is this marked as cluster-wide?
	RET			;[7.1179] No!  So return "not found"
	RETSKP			;Return success

	ENDSV.
	SUBTTL	Support Routines -- HDRFIQ (Fill in header from Q-Block)

;HDRFIQ - This routine is used to fill in the header portions of the VRQA
;         given only a Q-Block address.  It is called from EQEOKR and EQQSKD.
;
;	T1/ Address of Q-Block
;
;	CALL HDRFIQ
;
;Returns +1:	Always with VRQA header areas updated and with:
;		T2/ Start address in VRQA at which to add Additional Data
;
;Note  that  this  routine preserves T1. The following is the register
;convention used:
;
;	P1/ Address of Q-Block
;	P2/ Address of VRQA
;	P3/ Address of Lock-Block
;	T3 and T4 are work registers

;	XSWAPCD

HDRFIQ:	SAVEAC <T1,P1,P2,P3>	;Preserve T1 and get work registers
	MOVE P1,T1		;Get address of Q-Block
	XMOVEI P2,VRQA		;Get the address of the VRQA
	LOAD P3,ENQLBP,(P1)	;Get Lock-Block address
	MOVEI T4,EHDRSZ		;Get the size of the ENQSRV Header
	STOR T4,EBDOFF,(P2)	;Save as offset to .EBSOD word
	SETZRO EBFLAG,(P2)	;Clear the flags
	LOAD T4,ENQFLG,(P3)	;Get flags from Lock-Block
	TXNN T4,EN.TXT		;Is this lock described by a text string?
	IFSKP.
	  SETONE EQTXT,(P2)	;Yes so indicate this in VRQA
	ENDIF.
	MOVEI T4,.CFENQ		;Get function code for CFS
	STOR T4,EBCFSC,(P2)	;Put in VRQA
	MOVE T4,MYPOR4		;Get our node number
	STOR T4,EBNODE,(P2)	;Put in VRQA
	LOAD T4,ENQFLG,(P1)	;Get flags from Q-Block
	STOR T4,EBQFLG,(P2)	;Put in VRQA
	LOAD T4,ENQGRP,(P1)	;Get the group number from Q-Block
	STOR T4,EBGRP,(P2)	;Put in VRQA
	LOAD T3,ENQOFN,(P3)	;Get the OFN from the Lock-Block
	STOR T3,EBTYPE,(P2)	;Put in VRQA
	HRRES T3		;Extend the sign
	SKIPGE T3		;Is this an OFN or a non-file lock?
	IFSKP.			;An OFN
	  LOAD T4,STRX,(T3)	;Get the structure number
	  MOVE T4,STRTAB(T4)	;Get SDB address
	  SKIPE SDBALS(T4)	;Is there an alias for this one?
	  SKIPA T4,SDBALS(T4)	;Yes, so use it
	  MOVE T4,SDBNAM(T4)	;No, so use physical name
	  STOR T4,EBSTRN,(P2)	;Put in VRQA
	  LOAD T4,STGADR,SPTH(T3)  ;Get index block address
	  STOR T4,EBADDR,(P2)	;Put in VRQA
	ELSE.			;A non-file object
	  SETZRO EBSTRN,(P2)	;Clear both the structure name ...
	  SETZRO EBADDR,(P2)	;... and the index block address in VRQA
	ENDIF.
	LOAD T4,ENQHSH,(P3)	;Get hash value from Lock-Block
	STOR T4,EBHASH,(P2)	;Put in VRQA
	LOAD T4,ENQLEN,(P3)	;Get the length of the Lock-Block
	SUBI T4,LBLEN		;Compute length of string or user code
	STOR T4,EBTSWS,(P2)	;Put in VRQA
	LOAD T3,ENQNMS,(P3)	;Get length of mask block from Lock-Block
	STOR T3,EBMBWS,(P2)	;Put in VRQA
	ADD T4,T3		;Add it to the running total
	ADDI T4,EHDRSZ+LDCHDR	;Add in the constant header portion
	STOR T4,EBTOTT,(P2)	;Put in VRQA as total size less Additional Data
	LOAD T2,ENQMSK,(P1)	;Get the pointer to the mask block
	MOVEI T3,.EBSMB(P2)	;Setup destination in VRQA
	LOAD T1,EBMBWS,(P2)	;Get length of mask block
	CALLX (MSEC1,XBLTAT)	;(T1,T2,T3/T1,T2,T3)Move mask block into VRQA
	MOVE T2,P3		;Get address of Lock-Block
	ADDI T2,.ENTXT		;Offset to beginning of string in Lock-Block
	LOAD T1,EBTSWS,(P2)	;Get length of string
	CALLX (MSEC1,XBLTAT)	;(T1,T2,T3/T1,T2,T3)Move string into the VRQA
	MOVE T2,T3		;Return pointer to where Additional Data goes
	RET			;Done
	SUBTTL	Support Routines -- HDRFIL (Fill in header from Lock-Block)

;HDRFIL - This routine is used to fill in the header portions of the VRQA
;         given only a Lock-Block address.  It is called from EQCNTS, EQPOOL,
;	  and EQLKSD.
;
;	T1/ Address of Lock-Block
;
;	CALL HDRFIL
;
;Returns +1:	Always with VRQA header areas updated and with:
;		T2/ Start address in VRQA at which to add Additional Data
;
;Note  that  this  routine preserves T1. The following is the register
;convention used:
;
;	P1/ Address of Lock-Block
;	P2/ Address of VRQA
;	T3 and T4 are work registers

;	XSWAPCD

HDRFIL:	SAVEAC <T1,P1,P2>	;Preserve T1 and get work registers
	MOVE P1,T1		;Get address of Lock-Block
	XMOVEI P2,VRQA		;Get the address of the VRQA
	MOVEI T4,EHDRSZ		;Get the size of the ENQSRV Header
	STOR T4,EBDOFF,(P2)	;Save as offset to .EBSOD word
	SETZRO EBFLAG,(P2)	;Clear the flags
	MOVEI T4,.CFENQ		;Get function code for CFS
	STOR T4,EBCFSC,(P2)	;Put in VRQA
	MOVE T4,MYPOR4		;Get our node number
	STOR T4,EBNODE,(P2)	;Put in VRQA
	LOAD T4,ENQFLG,(P1)	;Get flags from Lock-Block
	STOR T4,EBQFLG,(P2)	;Put in VRQA
	TXNN T4,EN.TXT		;Is this lock described by a text string?
	IFSKP.
	  SETONE EQTXT,(P2)	;Yes so indicate this in VRQA
	ENDIF.
	SETZRO EBGRP,(P2)	;No group number is sent - record in VRQA
	LOAD T3,ENQOFN,(P1)	;Get the OFN from the Lock-Block
	STOR T3,EBTYPE,(P2)	;Put in VRQA
	HRRES T3		;Extend the sign
	SKIPGE T3		;Is this an OFN or a non-file lock?
	IFSKP.			;An OFN
	  LOAD T4,STRX,(T3)	;Get the structure number
	  MOVE T4,STRTAB(T4)	;Get SDB address
	  SKIPE SDBALS(T4)	;Is there an alias for this one?
	  SKIPA T4,SDBALS(T4)	;Yes, so use it
	  MOVE T4,SDBNAM(T4)	;No, so use physical name
	  STOR T4,EBSTRN,(P2)	;Put in VRQA
	  LOAD T4,STGADR,SPTH(T3)  ;Get index block address
	  STOR T4,EBADDR,(P2)	;Put in VRQA
	ELSE.			;A non-file object
	  SETZRO EBSTRN,(P2)	;Clear both the structure name ...
	  SETZRO EBADDR,(P2)	;... and the index block address in VRQA
	ENDIF.
	LOAD T4,ENQHSH,(P1)	;Get hash value from Lock-Block
	STOR T4,EBHASH,(P2)	;Put in VRQA
    	LOAD T4,ENQLEN,(P1)	;Get the length of the Lock-Block
	SUBI T4,LBLEN		;Compute length of string or user code
	STOR T4,EBTSWS,(P2)	;Put in VRQA
	SETZRO EBMBWS,(P2)	;No mask block is sent - record in VRQA
	ADDI T4,EHDRSZ+LDCHDR	;Add in the constant header portion
	STOR T4,EBTOTT,(P2)	;Put in VRQA as total size less Additional Data
	MOVE T2,P1		;Get address of Lock-Block
	ADDI T2,.ENTXT		;Offset to beginning of string in Lock-Block
	MOVEI T3,.EBSMB(P2)	;Setup destination in VRQA
	LOAD T1,EBTSWS,(P2)	;Get length of string
	CALLX (MSEC1,XBLTAT)	;(T1,T2,T3/T1,T2,T3)Move string into the VRQA
	MOVE T2,T3		;Return pointer to where Additional Data goes
	RET			;Done
	SUBTTL	Support Routines -- HDRFIP (Fill in header from P registers)

;HDRFIP - This routine is used to fill in the header portions of the VRQA
;         given only the P registers, Q1, and hash index.  It is only called
;	  from EQRSTS.
;
;	T1/ Hash value for Lock-Block
;	P1-P5 set up by call to VALREQ and HASH
;	Q1/ Address of lock request in user's space; set up by VALARG
;
;	CALL HDRFIP
;
;Returns +1:	Always with VRQA header areas updated and with:
;		T2/ Start address in VRQA at which to add Additional Data
;
;Note  that  this  routine preserves T1. The following is the register
;convention used:
;
;	Q1/ Address of VRQA

;	XSWAPCD

HDRFIP:	SAVEAC <T1,Q1,Q2>	;Preserve T1 and get a work register
	MOVE Q2,Q1		;[7.1103] Save the user's address for a moment
	XMOVEI Q1,VRQA		;Get the address of the VRQA
	STOR T1,EBHASH,(Q1)	;Put hash value in VRQA
	MOVEI T4,EHDRSZ		;Get the size of the ENQSRV Header
	STOR T4,EBDOFF,(Q1)	;Save as offset to .EBSOD word
	SETZRO EBFLAG,(Q1)	;Clear the flags
	MOVEI T4,.CFENQ		;Get function code for CFS
	STOR T4,EBCFSC,(Q1)	;Put in VRQA
	MOVE T4,MYPOR4		;Get our node number
	STOR T4,EBNODE,(Q1)	;Put in VRQA
	HLRZ T4,P1		;Get flags from P1
	STOR T4,EBQFLG,(Q1)	;Put in VRQA
	MOVEI T1,1		;Get length of user code word
	MOVEI T2,.ENQUC-.ENQLV(Q2)  ;[7.1103] Get location of user code
	LOAD T4,NMFLG,P2	;Get bits 0-2 to see if user specified code
	CAIN T4,NUMVAL		;5B2 cannot be a string pointer
	IFSKP.			;This is a text string
	  SETONE EQTXT,(Q1)	;Yes so indicate this in VRQA
	  MOVE T1,P4		;Get string length instead of user code length
	  HRRZ T2,P2		;Get location of string from pointer
	ENDIF.
	STOR T1,EBTSWS,(Q1)	;Put string/user-code length in VRQA
	MOVEI T3,.EBSMB(Q1)	;Setup destination in VRQA
	CALLX (MSEC1,BLTUM1)	;(T1,T2,T3/T1,T2,T3)Move string into the VRQA
	MOVEM T3,Q2		;Save pointer to where Additional Data goes
	SETZRO EBMBWS,(Q1)	;No mask block is sent - record in VRQA
	HRRZ T4,P5		;Get the group from P5
	STOR T4,EBGRP,(Q1)	;Put in VRQA
	HRRZ T3,P1		;Get the OFN from P1
	STOR T3,EBTYPE,(Q1)	;Put in VRQA
	HRRES T3		;Extend the sign
	SKIPGE T3		;Is this an OFN or a non-file lock?
	IFSKP.			;An OFN
	  LOAD T4,STRX,(T3)	;Get the structure number
	  MOVE T4,STRTAB(T4)	;Get SDB address
	  SKIPE SDBALS(T4)	;Is there an alias for this one?
	  SKIPA T4,SDBALS(T4)	;Yes, so use it
	  MOVE T4,SDBNAM(T4)	;No, so use physical name
	  STOR T4,EBSTRN,(Q1)	;Put in VRQA
	  LOAD T4,STGADR,SPTH(T3)  ;Get index block address
	  STOR T4,EBADDR,(Q1)	;Put in VRQA
	ELSE.			;A non-file object
	  SETZRO EBSTRN,(Q1)	;Clear both the structure name ...
	  SETZRO EBADDR,(Q1)	;... and the index block address in VRQA
	ENDIF.
	LOAD T4,EBTSWS,(Q1)	;Get string length in VRQA
	ADDI T4,EHDRSZ+LDCHDR	;Add in the constant header portion
	STOR T4,EBTOTT,(Q1)	;Put in VRQA as total size less Additional Data
	MOVE T2,Q2		;Return pointer to where Additional Data goes
	RET			;Done
	SUBTTL	Vote Requester -- ASK4IT (Send out votes)

;ASK4IT - Routine called by Interface Routines to send out a Request 
;	  Message Set to every TOPS-20 node running version 7.0 or  
;	  greater.
;
;This routine expects the VRQA to have been completed with the Request
;to be sent. Also, it is to be called with the ENQ Database Lock Token
;obtained (thus, NOINT).
;
;	CALL ASK4IT
;
;Returns +1:	Failure, error code in T1
;        +2:	Success, results of the vote in the VRPA
;
;Upon  successful  return  from  this routine, the VRPA will have been
;updated with the results of the vote. This is done by  various  other
;Vote Requester routines.

;	XSWAPCD

ASK4IT:	TRVAR <BUFL,BUFN,RTN,REMTOT,REMSUC>
	SAVEQ			;Get some work registers
ASKTOP:	SETZM VRPA		;Clear the entire VRPA
	MOVE T1,[VRPA,,VRPA+1]
	BLT T1,VRPA+VPSIZE-1
	XMOVEI Q3,VRPA
	SETONE VPNOV,(Q3)	;Set bit EQ.NOV in the VRPA
	AOS T1,VOTUNI		;Get a new unique code
	STOR T1,VPUNIQ,(Q3)
	SETZM VOTVCT		;Not waiting on any nodes
ASKLOP:	CIOFF			;Lock out connection state changes
        SKIPN CFSCMC		;Any connects outstanding?
	IFSKP.			;If so
	  CION			;Release lock
	  MOVEI T1,DISET	;Wait for outstanding connects ...
	  HRLI T1,CFSCMC	;... to go to zero
	  MDISMS
	  JRST ASKLOP		;Check again
	ENDIF.
	SKIPN T1,DLYLOK		;A vote delay in effect?
	IFSKP.			;If so
	  SUB T1,TODCLK		;Get relative time
	  IFG. T1		;If we still need to wait...
	    CION		;Release lock
	    CALLX (MSEC1,SETBKT);(T1/T1)Compute wait time
	    HRRI T1,CFRCNW	;Wait for a while
	    MDISMS
	    JRST ASKLOP		;Do all checks again
	  ENDIF.
	ENDIF.
	SKIPE CFSHCT		;Are there any other nodes out there?
	IFSKP.			;No, so quit now
	  CION			;Release lock
	  RETSKP		;Return success
	ENDIF.
	CION			;Allow connect state changes again
	MOVSI Q1,-HSTSIZ	;Loop over the entire CFSHST table

;Now, scan the CFSHST table and send a Request Message Set to every
;TOPS-20 node running version 7.0 or greater in the cluster.

ASKVOT:	TMNE VPNO,(Q3)		;Has the vote already been denied?
	JRST ASKCHK		;[8848] Yes, stop and wait for all replies
	SKIPLE T1,CFSHST(Q1)	;Get the CID
	SKIPL CFHSTS(Q1)	;Is there a valid CID in this slot
	JRST ASKNXT		;No, try next slot
	CALL <XENT SC.NOD>	;(T1/T1,T2)Get the CI node number for CID
	SKIPGE T2		;Was this a valid CID?
	JRST ASKNXT		;No skip it and do next slot
	CALLX (MSEC1,IS7020)	;(T2/)Is this a version 7.0 or greater node?
	 JRST ASKNXT		;No, so skip it

;This node can be sent to.  Format the VRQA into a Request Message Set
;and send it.

	XMOVEI T1,VRQA		;Get the address of VRQA
	LOAD T2,EBTOTT,(T1)	;Get the total number of user words in VRQA
	SUBI T2,EHDRSZ+LDCHDR	;Remove header amounts
	HRLI T2,.EBSMB-.MHUDA	;Starting location is mask block
	ADDI T1,.EBSMB		;Offset to start of data to be transfered
	CALL SC.BRK		;(T1,T2/T1,T2,T3)Break into SCA message buffers
	 RETBAD (ENQX24)	;Error - must have no more SCA space
	MOVEM T1,BUFL		;Save the buffer list address
	MOVEM T2,BUFN		;Save the number of buffers
	MOVEM T3,RTN		;Save the return address
;The Mask Block and ASCIZ String have been placed in SCA message buffer(s).
;Update the header portions of the buffers and send them to the node.

	MOVE T1,.PKFLI(T1)	;Get the next buffer in the list
	EXCH T1,BUFL		;It is next buffer and BUFL is current buffer
	MOVEM T3,-C%BINV(T1)	;Put return address in Invisible Area
	CALL BUFHDR		;(T1/T1)Update various header values
	XMOVEI T2,VRQA		;Get the address of the VRQA
	MOVE T3,BUFN		;Get total number of buffers
	STOR T3,EBPKTN,(T1)	;Put in message buffer
	MOVEI T3,1		;Say that this ...
	STOR T3,EBPNUM,(T1)	;... is the first packet
	MOVE T3,VOTUNI		;Get unique code
	STOR T3,EBUNIQ,(T1)	;Put in message buffer
	LOAD T3,EBTOTT,(T2)	;Get total number of user words from VRQA
	CAILE T3,C%MUDA		;Is it greater than maximum possible?
	MOVEI T3,C%MUDA		;Yes, so use maximum for 1st buffer
	STOR T3,EBTOTT,(T1)	;Put in message buffer
	LOAD T4,EBTOTT,(T2)	;Get total again from VRQA
	SUB T4,T3		;Reduce to reflect remaining String and Data
	MOVEM T4,REMTOT		;Remember the remaining amount to send
	SUBI T3,EHDRSZ+LDCHDR	;Take out constant header part
	LOAD T4,EBMBWS,(T2)	;Get size of Mask Block
	SUB T3,T4		;Take away - this is max string size for buffer
	LOAD T4,EBTSWS,(T2)	;Get the total size of the string
	SUB T4,T3		;Take max size away from total size
	IFLE. T4		;Does the string fit in the block?
	  SETZ T4,		;Yes, so left over amount is zero
	  LOAD T3,EBTSWS,(T2)	;And the size of the string is in VRQA
	ENDIF.
	MOVEM T4,REMSUC		;Store left over as remaining String to send
	STOR T3,EBTSWS,(T1)	;Store the string size in the buffer

;Now the header portion of the first buffer is complete.  The entire Mask
;Block, and as much of the String and Addtional Data as can fit is also
;present.  Note that REMTOT now has the number of user words left 
;to send (less the constant header amount).  REMSUC has the number
;of words of String left to send.  If this is zero, but REMTOT is not,
;then what is left to send is Additional Data.
;
;The buffer, which was obtained from SCA free space, is sent with F.RTB set
;so that it does not go on the port's message free queue.  When the .SSMSC 
;callback occurs, the return address stored in the packet (should be SC.RBF)
;will be called to return the buffer.
;
;Note:  The value of C%OVHW+.PKLEN is subtracted from the total because SCA
;and PHYKLP add it on to account for the message buffer overhead. But, this
;value is already included in the .MHUDA symbol. So, the maximum value  for
;a  message  length  which can be specified to SCA is C%MUDA-C%OVHW-.PKLEN.
;Otherwise a KLPERR due  to  invalid  packet  length  or  a  SCAPTL  BUGHLT
;results. It appears that this behavior is incorrect and that it is sending
;C%OVHW+.PKLEN extra words for every message packet!
	LOAD T3,EBTOTT,(T1)	;Get the number of user words
	MOVE Q2,T1		;Save buffer address in case of failure
	BLCAL. (<XENT SC.SMG>,<CFSHST(Q1),[F.RTB+F.SPM],T3,T1,[CFSPRI],[0],[0]>)
	IFNSK.			;Some kind of error
	  MOVE T1,Q2
	  CALL @-C%BINV(T1)	;(T1/)Return the buffer
	  SKIPE T1,BUFL		;Is there another buffer?
	  CALL @RTN		;(T1/)Yes, return it too
	  JRST ASKTOP		;Try again
	ENDIF.
	SKIPN T1,BUFL		;Is there another buffer?
	JRST ASKSDN		;No, try another node

;The Request Message Set requires 2 SCA buffers.  First, do some sanity 
;checking.

	SKIPG T2,REMTOT		;Any remaining user words to send
	BUG.(HLT,EQSTOT,ENQSRV,SOFT,<ENQSRV - VRQA value of EBTOTT is too small>,,<

Cause:	The value in variable BUFL indicates that SC.BRK thought that the
	VRQA would require multiple buffers.  But, the value of EBTOTT is 
	less than or equal to zero which means that there is nothing more 
	to send.

Action:	Examine the dump for signs of a miscalculation in either SC.BRK
	or the setting of EBTOTT.
>)
	CAILE T2,C%MUDA		;Will they fit in just one more buffer?
	BUG.(HLT,EQLTOT,ENQSRV,SOFT,<ENQSRV - VRQA value of EBTOTT is too big>,,<

Cause:	The value in variable BUFL indicates that SC.BRK thought that the
	VRQA would require multiple buffers.  But, the value of EBTOTT 
	indicates that the rest to send will not fit in just 1 more buffer.

Action:	Examine the dump for signs of a miscalculation in either SC.BRK
	or the setting of EBTOTT.
>)
	SKIPE .PKFLI(T1)	;Is there yet another buffer?
	BUG.(HLT,EQBLNK,ENQSRV,SOFT,<ENQSRV - Bad list of SCA buffers>,,<

Cause:	The .PKFLI pointer in the second buffer points to yet another 
	buffer.  Presently, the code only expects to send 2 SCA messages
	for any Request Message Set.

Action:	Examine the dump for signs of a miscalculation in SC.BRK.
>)
	MOVE T3,RTN		;Get the return address
	MOVEM T3,-C%BINV(T1)	;Put return address in Invisible Area
	CALL BUFHDR		;(T1/T1)Update various header values
	XMOVEI T2,VRQA		;Get the address of the VRQA
	MOVE T3,BUFN		;Get total number of buffers
	STOR T3,EBPKTN,(T1)	;Put in message buffer
	STOR T3,EBPNUM,(T1)	;Say this is the second and last packet
	MOVE T3,VOTUNI		;Get unique code
	STOR T3,EBUNIQ,(T1)	;Put in message buffer
	MOVE T3,REMSUC		;Get the total size of the string remaining
	STOR T3,EBTSWS,(T1)	;Store the string size in the buffer
	SETZRO EBMBWS,(T1)	;No mask block is sent
	MOVE T3,REMTOT		;Get total number of user words remaining
	ADDI T3,EHDRSZ+LDCHDR	;Add on the constant header portion
	STOR T3,EBTOTT,(T1)	;Put in message buffer

;Now send the second and last buffer.

	MOVE Q2,T1		;Save buffer address in case of failure
	BLCAL. (<XENT SC.SMG>,<CFSHST(Q1),[F.RTB+F.SPM],T3,T1,[CFSPRI],[0],[0]>)
	IFNSK.			;Some kind of error
	  MOVE T1,Q2
	  CALL @-C%BINV(T1)	;(T1/)Return the buffer
	  JRST ASKTOP		;Try again
	ENDIF.
ASKSDN:	AOS VOTVCT		;Yes, waiting for another reply
ASKNXT:	AOBJN Q1,ASKVOT		;Try the next CFS host slot

;The votes have now been sent to all the nodes.  Wait for replies.

ASKCHK:	TMNE VPRTY,(Q3)		;Did a cluster-state change occur
	JRST ASKTOP		;Yes, restart vote
	SKIPN VOTVCT		;Are all the votes in?
	RETSKP			;Yes, so return now

;Not all the replies are in yet.  The fork will wait in EVWAIT until
;they all arrive, a cluster state change occurs, or a denied reply
;arrives.

	MOVEI T1,EVWAIT		;Get address of wait routine
	MDISMS			;Wait
	JRST ASKCHK		;Check on response

	ENDTV.
	SUBTTL	Vote Requester -- ASK4IT (Send out votes) -- EVWAIT (Scheduler test)

;EVWAIT - Scheduler test used by ASK4IT.  Causes the fork to wake up when
;	  all the outstanding replies have arrived or a cluster state change
;	  occurs.

	RESCD

EVWAIT:	SKIPN VOTVCT		;Are all the votes in?
	JRST 1(T4)		;Yes, so wake up
	MOVEI T1,VRPA		;Get address of VRPA
	TMNE VPRTY,(T1)		;Has a cluster state change occurred?
	JRST 1(T4)		;Yes, so wake up
	JRST 0(T4)		;No need to wake up yet
	SUBTTL	Vote Requester -- ASK4IT (Send out votes) -- BUFHDR (Fill in Buffer Header)

;BUFHDR - Routine used to transfer various header values from the VRQA 
;	  into an SCA message buffer header portion
;
;       T1/ Address of SCA message buffer
;
;	CALL BUFHDR
;
;Returns +1:	Always, with the following values updated in the SCA
;		message buffer: EBDOFF, EBFLAG, EBCFSC, EBNODE, EBFTYP,
;		EBQFLG, EBTYPE, EBSTRN, EBADDR, EBGRP, EBHASH, EBMBWS.
;
;Preserves T1.

 	XSWAPCD

BUFHDR:	SAVEAC <T1>		;Preserve T1
	XMOVEI T2,VRQA		;Get the address of the VRQA
	LOAD T3,EBDOFF,(T2)	;Transfer ...
	STOR T3,EBDOFF,(T1)	;... field EBDOFF from VRQA to buffer
	LOAD T3,EBFLAG,(T2)	;Transfer ...
	STOR T3,EBFLAG,(T1)	;... field EBFLAG from VRQA to buffer
	LOAD T3,EBCFSC,(T2)	;Transfer ...
	STOR T3,EBCFSC,(T1)	;... field EBCFSC from VRQA to buffer
	LOAD T3,EBNODE,(T2)	;Transfer ...
	STOR T3,EBNODE,(T1)	;... field EBNODE from VRQA to buffer
	LOAD T3,EBFTYP,(T2)	;Transfer ...
	STOR T3,EBFTYP,(T1)	;... field EBFTYP from VRQA to buffer
	LOAD T3,EBQFLG,(T2)	;Transfer ...
	STOR T3,EBQFLG,(T1)	;... field EBQFLG from VRQA to buffer
	LOAD T3,EBTYPE,(T2)	;Transfer ...
	STOR T3,EBTYPE,(T1)	;... field EBTYPE from VRQA to buffer
	LOAD T3,EBSTRN,(T2)	;Transfer ...
	STOR T3,EBSTRN,(T1)	;... field EBSTRN from VRQA to buffer
	LOAD T3,EBADDR,(T2)	;Transfer ...
	STOR T3,EBADDR,(T1)	;... field EBADDR from VRQA to buffer
	LOAD T3,EBGRP,(T2)	;Transfer ...
	STOR T3,EBGRP,(T1)	;... field EBGRP from VRQA to buffer
	LOAD T3,EBHASH,(T2)	;Transfer ...
	STOR T3,EBHASH,(T1)	;... field EBHASH from VRQA to buffer
	LOAD T3,EBMBWS,(T2)	;Transfer ...
	STOR T3,EBMBWS,(T1)	;... field EBMBWS from VRQA to buffer
	RET			;All done
	SUBTTL	Vote Requester -- Dispatch Table (EIDSPT)

;This  dispatch  table  is invoked from EQMSG when the reply to a vote
;request arrives

 	XRESCD

EIDSPT:	XADDR. (EIEOKR)		;.EOKR
	XADDR. (EICNTS)		;.CNTS
	XADDR. (EIRSTS)		;.RSTS
	XADDR. (EIPOOL)		;.POOL
	XADDR. (EIQSKD)		;.QSKD
	XADDR. (EILKSD)		;.LKSD
	SUBTTL	Vote Requester -- EIEOKR (Reply to .EOKR vote)

;EIEOKR - Routine which processes the replies to the vote request 
;         generated by the EVEOKR routine.
;
;	Q1/ Address of incoming SCA message buffer (the reply)
;
;Returns +1:	Always, with VRPA updated

;	XRESCD

EIEOKR:	XMOVEI T1,VRPA		;Get address of VRPA
	TMNE VPNO,(T1)		;Has a no vote already arrived?
	RET			;Yes, so just return
	TMNN EQNO,(Q1)		;Was the reply a "NO"?
	IFSKP.			;Yes
	  SETONE VPNO,(T1)	;Say it was denied in VRPA
	  MOVE T2,.EBAD1(Q1)	;Get error code returned
	  MOVEM T2,.VPAD1(T1)	;Put in VRPA
	ENDIF.
	TMNE EQNOV,(Q1)		;Did the other node know about this lock?
	IFSKP.			;Yes
	  SETZRO VPNOV,(T1)	;Clear "no vote required" bit in VRPA
	ENDIF.
	RET			;Done
	SUBTTL	Vote Requester -- EICNTS (Reply to .CNTS vote)

;EIEOKR - Routine which processes the replies to the vote request 
;         generated by the EQCNTS routine.
;
;	Q1/ Address of incoming SCA message buffer (the reply)
;
;Returns +1:	Always, with VRPA updated

;	XRESCD

EICNTS:	XMOVEI T1,VRPA		;Get address of VRPA
	MOVE T2,.EBAD1(Q1)	;Get the counts just returned
	ADDM T2,.VPAD1(T1)	;Add to the running total
	RET			;Done
	SUBTTL	Vote Requester -- EIRSTS (Reply to .RSTS vote)

;EIRSTS - Routine which processes the replies to the vote request 
;         generated by the EQRSTS routine.
;
;	Q1/ Address of incoming SCA message buffer (the reply)
;
;Returns +1:	Always, with VRPA updated

;	XRESCD

EIRSTS:	TMNE EQNO,(Q1)		;Was the reply a "NO"?
	RET			;Yes so just return
	XMOVEI T1,VRPA		;Get address of VRPA
	HLRZ T2,.EBAD2(Q1)	;Get the job number
	CAIN T2,-1		;Was there one?
	IFSKP.			;Yes
	  HRLM T2,.VPAD2(T1)	;Put in VRPA
	  HLRZ T2,.EBAD1(Q1)	;Get the lock access
	  HRLM T2,.VPAD1(T1)	;Put in VRPA
	ENDIF.
	HRRZ T2,.EBAD1(Q1)	;Get the level number
	HRRZ T3,.VPAD1(T1)	;Get the number in the VRPA
	CAMLE T2,T3		;Is it greater than what is there now?
	HRRM T2,.VPAD1(T1)	;Put in VRPA
	HRRZ T2,.EBAD2(Q1)	;Get number of sharers
	ADDM T2,.VPAD2(T1)	;Add to running total in VRPA
	MOVE T2,.EBAD3(Q1)	;Get the time stamp
	SKIPE .VPAD3(T1)	;Is there any value yet?
	CAMGE T2,.VPAD3(T1)	;Is it less than what is there now?
	MOVEM T2,.VPAD3(T1)	;Yes so put in VRPA
	RET			;Done
	SUBTTL	Vote Requester -- EIPOOL (Reply to .POOL vote)

;EIPOOL - Routine which processes the replies to the vote request 
;         generated by the EQPOOL routine.
;
;	Q1/ Address of incoming SCA message buffer (the reply)
;
;Returns +1:	Always, with VRPA updated

;	XRESCD

EIPOOL:	XMOVEI T1,VRPA		;Get address of VRPA
	MOVE T2,.EBAD1(Q1)	;Get the count just returned
	ADDM T2,.VPAD1(T1)	;Add to the running total
	RET			;Done
	SUBTTL	Vote Requester -- EIQSKD (Reply to .QSKD vote)

;EIQSKD - Routine which processes the replies to the vote request 
;         generated by the EVQSKD routine.
;
;	Q1/ Address of incoming SCA message buffer (the reply)
;
;Returns +1:	Always, with VRPA updated

;	XRESCD

EIQSKD:	XMOVEI T1,VRPA		;Get address of VRPA
	TMNE VPNO,(T1)		;Has a no vote already arrived?
	RET			;Yes, so just return
	TMNN EQNO,(Q1)		;Was the reply a "NO"?
	IFSKP.			;Yes
	  SETONE VPNO,(T1)	;Say it was denied in VRPA
	  RET			;Done
	ENDIF.
	MOVE T2,.EBAD1(Q1)	;Get the count just returned
	ADDM T2,.VPAD1(T1)	;Add to the running total
	RET			;Done
	SUBTTL	Vote Requester -- EILKSD (Reply to .LKSD vote)

;EILKSD - Routine which processes the replies to the vote request 
;         generated by the EQLKSD routine.
;
;	Q1/ Address of incoming SCA message buffer (the reply)
;
;Returns +1:	Always.  No action needs to be taken for this reply.
;		It is just needed so that it is assured that all the 
;		systems have received and processed the rescheduling
;		notification.

;	XRESCD

EILKSD:	RET			;The reply is meaningless
	SUBTTL	Vote Responder -- Dispatch Table (EVDSPT)

 	XSWAPCD

EVDSPT:	XADDR. (EVEOKR)		;.EOKR
	XADDR. (EVCNTS)		;.CNTS
	XADDR. (EVRSTS)		;.RSTS
	XADDR. (EVPOOL)		;.POOL
	XADDR. (EVQSKD)		;.QSKD
 	XADDR. (EVLKSD)		;.LKSD
	SUBTTL	Vote Responder -- ANSWER (Send the Reply)

;ANSWER - Send the Reply to a Request Message Set
;
;This  routine  is called from each of the dispatch routines in EVDSPT
;in order to send the reply to the vote just received. It expects that
;the VANA contains the reply to be sent. The VANA will be  transferred
;into  the  VRB  and  sent  back to the node which sent us the Request
;Message Set. Also, RPLYND is expected to contain the CFS  host  index
;from  which to get the CID of the node to receive the reply. The left
;half of EQFKFL contains the unique code of the vote which  we  should
;be replying to.
;
;	CALL ANSWER
;
;Returns +1:	Always
;
;[7.1115] Note:  The  unique code in the VANA should match the unique code
;[7.1115] in the VRQA. The VRQA unique code is also contained in the  left
;[7.1115] half of EQFKFL. If it does not, then a cluster state change must
;[7.1115] have  occurred  and  another  node is now revoting on one of its
;[7.1115] blocks on the EQLBLT. So, the VRQA contains a new  vote  request
;[7.1115] to  reply  to  and  the  one in the VANA is not valid and can be
;[7.1115] ignored.

;	XSWAPCD

ANSWER:	XMOVEI T2,VANA		;[7.1115] Get the address of the VANA
	CIOFF			;Protect against state changes for a moment
	LOAD T1,EBUNIQ,(T2)	;[7.1115] Get unique code of vote in VANA
	HLRZ T3,EQFKFL		;Get the unique code of the vote to respond to
	CAMN T1,T3		;Are they the same?
	IFSKP.			;No, a state change happened previously
	  CION			;Unlock the CI
	  RET			;Return now without replying to old vote
	ENDIF.
	LOAD T1,EBTOTT,(T2)	;Get the total number of user words
	MOVE T3,VRBADR		;Get address to use for reply buffer
	ADDI T2,.MHUDA		;Beginning of ENQSRV Header in VRQA
	ADDI T3,.MHUDA		;Beginning of ENQSRV Header in VRB
	CALLX (MSEC1,XBLTAT)	;(T1,T2,T3/T1,T2,T3)Transfer VRQA into VRB
	MOVE T2,VRBADR		;Get VRB address again
	SETZRO EBMBWS,(T2)	;Replies never include a mask block ...
	SETZRO EBTSWS,(T2)	;... or a text string/user code
	LOAD T3,EBTOTT,(T2)	;Get the number of user words
	MOVE T1,RPLYND		;Get host index
	CION			;Unlock the CI for the reply
	BLCAL. (<XENT SC.SMG>,<CFSHST(T1),[F.RTB+F.SPM],T3,T2,[CFSPRI],[0],[0]>)
	IFNSK.			;[7.1142] Error - send failed!
	  CAIN T1,SCSNEC	;[7.1142] Did it fail due to lack of credit?
	  JRST ANSWER		;[7.1142] Yes, try again
	ENDIF.			;[7.1142]
	CIOFF			;[7.1142] Protect against state changes
	XMOVEI T2,VANA		;[7.1142] Get the address of the VANA
	LOAD T1,EBUNIQ,(T2)	;[7.1142] Get unique code of vote in VANA
	HLRZ T3,EQFKFL		;[7.1142] Get unique code of vote to respond to
	CAMN T1,T3		;[7.1142] Are they the same?
	HRRZS EQFKFL		;[7.1142] Reply completed - clear out unique code
	CION			;[7.1142] Allow interrupts again
	RET			;All done
	SUBTTL	Vote Responder -- EVEOKR (Respond to .EOKR vote)

;EVEOKR - This routine sends a reply to a vote request just received.
;         The vote is of type .EOKR which is sent by the EQEOKR routine.
;
;	The VANA contains the reassembled Request Message Set.
;
;Returns +1:	Always once the reply message is sent

;	XSWAPCD

EVEOKR:	SAVEAC <Q2>		;Save a work register
	XMOVEI Q2,VANA		;[7.1115] Get address of VANA
	SETZRO <EQNO,EQNOV>,(Q2)  ;Clear status flags in VRQA
	CALL LOCLOK		;(/T1)Find the Lock-Block for this vote
	IFNSK.			;There isn't one!
	  SETONE EQNOV,(Q2)	;No vote is required as far as we are concerned
	  JRST EVEOK7		;Send the approval
	ENDIF.
	TXNN T2,EN.LTL		;Long term lock?
	IFSKP.			;Yes
	  LOAD T2,ENQNLQ,(T1)	;Now see if the lock is free
	  CAME T2,T1		;It is free if it points to itself
	  ANSKP.		;Yes, it is free
	    SETONE EQNOV,(Q2)	;No vote is required as far as we are concerned
	    JRST EVEOK7		;Send the approval
	ENDIF.

;At this point, the Lock-Block is active on this system.  The same
;type of checks which occured in routine ENQ9 on the other node must
;now be performed here.

	LOAD T2,ENQNMS,(T1)	;Get size of mask block
	JUMPE T2,EVEOK5		;Skip checks if zero
	LOAD T3,EBMBWS,(Q2)	;Get mask block size of vote request
	MOVEM T3,T4		;Save this for later
	JUMPE T3,EVEOK5		;Skip checks if zero
	CAMN T2,T3		;They must be equal if non-zero
	IFSKP.			;They aren't
	  SETONE EQNO,(Q2)	;Set the "NO" bit in VRQA
	  MOVEI T1,ENQX23	;Get correct error code
	  MOVEM T1,.EBAD1(Q2)	;Put in as Additional Data
	  INCR EBTOTT,(Q2)	;Say one more word to send
	  JRST EVEOK7		;Refuse the request
	ENDIF.
EVEOK5:	LOAD T2,EBTSWS,(Q2)	;Get size of string
	ADDI T2,.MHUDA+EHDRSZ+LDCHDR  ;Add in constant header amount
	ADD T2,T4		;Add mask block size - now at Additional Data
	MOVE T2,VANA(T2)	;[7.1115] Get the first word of Additional Data
        TMNE EQBLN,(Q2)		;Are levels being checked
	JRST EVEOK6		;No, so skip the check
	HRRZM T2,T3		;Move level number into T3
	LOAD T4,ENQLVL,(T1)	;Get level number from Lock-Block
	CAMN T4,T3		;They should be the same
	IFSKP.			;They aren't!
	  SETONE EQNO,(Q2)	;Set the "NO" bit in VRQA
	  MOVEI T1,ENQX3	;Get correct error code
	  MOVEM T1,.EBAD1(Q2)	;Put in as Additional Data
	  INCR EBTOTT,(Q2)	;Say one more word to send
	  JRST EVEOK7		;Refuse the request
	ENDIF.
EVEOK6:	HLRZS T2		;Keep just pool total in T2
	LOAD T4,ENQTR,(T1)	;Get pool total from Lock-Block
	CAMN T4,T2		;They should be the same
	IFSKP.			;They aren't!
	  SETONE EQNO,(Q2)	;Set the "NO" bit in VRQA
	  MOVEI T1,ENQX4	;Get correct error code
	  MOVEM T1,.EBAD1(Q2)	;Put in as Additional Data
	  INCR EBTOTT,(Q2)	;Say one more word to send
	ENDIF.
EVEOK7:	CALLRET ANSWER		;()Send the reply
	SUBTTL	Vote Responder -- EVCNTS (Respond to .CNTS vote)

;EVCNTS - This routine sends a reply to a vote request just received.
;         The vote is of type .CNTS which is sent by the EQCNTS routine.
;
;	The VANA contains the reassembled Request Message Set.
;
;Returns +1:	Always once the reply message is sent

;	XSWAPCD

EVCNTS:	SAVEAC <Q2>		;Save a work register
	XMOVEI Q2,VANA		;[7.1115] Get address of VANA
	INCR EBTOTT,(Q2)	;Say one more word to send
	CALL LOCLOK		;(/T1)Find the Lock-Block for this vote
	IFNSK.			;There isn't one!
	  SETZM .EBAD1(Q2)	;Clear the first word of Additional Data
	  CALLRET ANSWER	;()Send the count of "0,,0"
	ENDIF.
	CALL CNTQLQ		;(T1/T1)Get count of "waiters,,lockers"
	MOVEM T1,.EBAD1(Q2)	;Return in first word of Additional Data
	CALLRET ANSWER		;()Send the reply
	SUBTTL	Vote Responder -- EVRSTS (Respond to .RSTS vote)

;EVRSTS - This routine sends a reply to a vote request just received.
;         The vote is of type .RSTS which is sent by the EQRSTS routine.
;
;	The VANA contains the reassembled Request Message Set.
;
;Returns +1:	Always once the reply message is sent

;	XSWAPCD

EVRSTS:	SAVEAC <Q1,Q2>		;Save work registers
	XMOVEI Q2,VANA		;[7.1115] Get address of VANA
	CALL LOCLOK		;(/T1)Find the Lock-Block for this vote
	IFNSK.			;There isn't one!
	  SETONE EQNO,(Q2)	;Set the "NO" bit in VRQA
	  CALLRET ANSWER	;()Send the reply
	ENDIF.
	MOVEM T1,Q1		;Remember the Lock-Block address
	CALL CNTQ		;(T1/T1)Get access!fork-number,,sharer-count
	SETZM .EBAD1(Q2)	;Assume shared access
	SKIPL T1		;Is it?
	HRROS .EBAD1(Q2)	;No, so set access as exclusive
	HRRM T1,.EBAD2(Q2)	;Store the number of sharers
	HLRZ T2,T1		;Get just access!fork
	TRZ T2,(1B0)		;Mask off access bit
	HRRZ T3,T1		;Get the number of sharers
	SKIPN T3		;Were there any sharers found?
	IFSKP.			;Yes
	  LOAD T1,FKJO%,(T2)	;Get job number of this fork
	  CALLX (MSEC1,LCL2GL)	;(T1/T1)Get global job number
	   SETO T1,		;No job found - return no job number for sharer
	ELSE.			;No sharers
	  SETO T1,		;So say owning job is -1
	ENDIF.
	HRLM T1,.EBAD2(Q2)	;Store job number in VRQA
	LOAD T1,ENQLVL,(Q1)	;Get the level number of this lock
	HRRM T1,.EBAD1(Q2)	;Put in VRQA
	LOAD T1,ENQTS,(Q1)	;Get the time stamp
	MOVEM T1,.EBAD3(Q2)	;Put in VRQA
	LOAD T1,EBTOTT,(Q2)	;Get the total size of VRQA
	ADDI T1,3		;Increment to reflect data just stored
	STOR T1,EBTOTT,(Q2)	;Put in VRQA
	CALLRET ANSWER		;()Send the reply
	SUBTTL	Vote Responder -- EVPOOL (Respond to .POOL vote)

;EVPOOL - This routine sends a reply to a vote request just received.
;         The vote is of type .POOL which is sent by the EQPOOL routine.
;
;	The VANA contains the reassembled Request Message Set.
;
;Returns +1:	Always once the reply message is sent

;	XSWAPCD

EVPOOL:	SAVEAC <Q2>		;Save a work register
	XMOVEI Q2,VANA		;[7.1115] Get address of VANA
	INCR EBTOTT,(Q2)	;Say one more word to send
	CALL LOCLOK		;(/T1)Find the Lock-Block for this vote
	IFNSK.			;There isn't one!
	  SETZM .EBAD1(Q2)	;Clear the first word of Additional Data
	  CALLRET ANSWER	;()Send the count of "0,,0"
	ENDIF.
	LOAD T3,ENQTR,(T1)	;Get total number of resources for lock
	LOAD T2,ENQRR,(T1)	;Get the number remaining
	SUB T3,T2		;Compute number used
	MOVEM T3,.EBAD1(Q2)	;Put in VRQA
	CALLRET ANSWER		;()Send the reply
	SUBTTL	Vote Responder -- EVQSKD (Respond to .QSKD vote)

;EVQSKD - This routine sends a reply to a vote request just received.
;         The vote is of type .QSKD which is sent by the EQQSKD routine.
;
;	The VANA contains the reassembled Request Message Set.
;
;Returns +1:	Always once the reply message is sent

;	XSWAPCD

EVQSKD:	SAVEAC <Q2>		;Save a work register
	XMOVEI Q2,VANA		;[7.1115] Get address of VANA
	SETZRO EQNO,(Q2)  	;Clear "NO" flag in VRQA
	CALL LOCLOK		;(/T1)Find the Lock-Block for this vote
	IFNSK.			;There isn't one!
	  SETZM .EBAD1(Q2)	;Send "0" as the number of pool resources used
	  INCR EBTOTT,(Q2)	;One word of Additional Data is sent
	  CALLRET ANSWER	;()Send the reply
	ENDIF.

;At this point, all the Q-Blocks which are visible and queued to the Lock-Block 
;must be checked against the request currently specified in the VRQA.  These 
;checks are identical to the ones done on the node which sent the vote request.  

	LOAD T3,EBQFLG,(Q2)	;Get flags
	LOAD T4,EBGRP,(Q2)	;Get the group number
	HRL T4,T3		;Put flags in correct position
	XMOVEI T2,.EBSMB(Q2)	;Get address of Mask block
	LOAD T3,EBMBWS,(Q2)	;Get size of Mask Block
	SKIPN T3		;Is there one?
	SETZ T2,		;No, so do not send an address
	CALL QSKDRC		;(T1,T2,T3,T4/T1)Check mask blocks and sharer groups
	IFNSK.			;There was some incompatibility
	  SETONE EQNO,(Q2)	;Say "NO" to drugs!
	  CALLRET ANSWER	;()Send the reply
	ENDIF.
	LOAD T3,ENQTR,(T1)	;Get total number of resources for lock
	LOAD T2,ENQRR,(T1)	;Get the number remaining
	SUB T3,T2		;Compute number used
	MOVEM T3,.EBAD1(Q2)	;Put in VRQA
	INCR EBTOTT,(Q2)	;One word of Additional Data is sent
	CALLRET ANSWER		;()Send the reply
	SUBTTL	Vote Responder -- EVLKSD (Respond to .LKSD vote)

;EVLKSD - This routine sends a reply to a vote request just received.
;         The vote is of type .LKSD which is sent by the EQLKSD routine.
;
;	The VANA contains the reassembled Request Message Set.
;
;Returns +1:	Always once the reply message is sent

;	XSWAPCD

EVLKSD:	CALL LOCLOK		;(/T1)Find the Lock-Block for this vote
	 CALLRET ANSWER		;There isn't one!  Ignore notification.
	LOAD T2,ENQFLG,(T1)	;Get the flags from the Lock-Block
	TXON T2,EN.SDO		;Indicate that scheduling is needed
	AOS EQFKFL		;Count this only if not already set
	STOR T2,ENQFLG,(T1)	;Replace the flags
	CALLRET ANSWER		;()Send back a null reply
      SUBTTL Interface Routines for ENQ -- EQEOKR (Check ENQ% request)

;This  routine is called from ENQREQ to remotely check the validity of
;the lock request (Q-Block) which is being specified in the ENQ% call.
;Specifically, the following is checked:
;
; 1.  Mask Block size compatibility
; 2.  Level Number agreement
; 3.  Pool total agreement
;
;	T1/ Q-Block address
;
;       CALL EQEOKR
;
;Returns +1:	Failure, with T1/ Error code
;        +2:	Success, Q-Block can now be scheduled, with:
;		T1/ Q-Block address
;
;The error codes which can be returned by this routine are:
;
;    ENQX3:  Request and lock level numbers do not match
;    ENQX4:  Number of pool and lock resources do not match
;    ENQX23:  Mismatched mask block lengths

;	XSWAPCD

EQEOKR::STKVAR <LBADDR,QBADDR>
	MOVE T2,ENQWRD		;Get the ENQ word for this process
	TXNN T2,EQ%ENA		;Is cluster-wide capability enabled?
	RETSKP			;No, so just return
	LOAD T2,ENQFLG,(T1)	;Get flags for Q-Block
	TXNN T2,EN.CLL		;Is this a cluster-wide request?
	RETSKP			;No, so just return
	LOAD T2,ENQLBP,(T1)	;Get Lock-Block address
	LOAD T2,ENQFLG,(T2)	;Get flags
	TXNE T2,EN.NOV		;Do we need to vote on this lock?
	RETSKP			;No, so just return
	MOVEM T1,QBADDR		;Save Q-Block address
	CALL HDRFIQ		;(T1/T1,T2)Fill in most of the VRQA header
	LOAD T3,ENQLBP,(T1)	;Get Lock-Block address
	MOVEM T3,LBADDR		;Save it for later
	LOAD T4,ENQTR,(T3)	;Get the pool total
	HRLM T4,(T2)		;Position in Additional Data
	LOAD T4,ENQLVL,(T3)	;Get level number of lock
	HRRM T4,(T2)		;Position in Additional Data
	XMOVEI T2,VRQA		;Get address of VRQA
	INCR EBTOTT,(T2)	;Add one to user total because of data
	TXNN P1,EN%BLN		;Is the user bypassing level checking?
	IFSKP.			;Yes
	  SETONE EQBLN,(T2)	;Indicate in flags
	ENDIF.
	MOVEI T3,.EOKR		;Get function code
	STOR T3,EBFTYP,(T2)	;Store in VRQA
	CALL ASK4IT		;()Send the VRQA to all systems
	 RETBAD ()		;Failed due to lack of resources
	XMOVEI T1,VRPA		;Get address of reply area
	TMNN VPNOV,(T1)		;Are subsequent votes required for this lock?
	IFSKP.			;No
	  MOVE T2,LBADDR	;Get Lock-Block address
	  LOAD T3,ENQFLG,(T2)	;Get flags
	  TXO T3,EN.NOV		;Set "no vote required" bit
	  STOR T3,ENQFLG,(T2)	;Restore flags
	  TMNE VPNO,(T1)	;Is the "NO" bit also set?
	  BUG.(HLT,EQNOVW,ENQSRV,SOFT,<ENQSRV - NO bit set no vote required>,,<

Cause:	The VRPA contains the replies to a vote request and VPNO and VPNOV
	are both set.  This should never happen since VPNOV means that no 
	other node know about the lock but VPNO means that another node 
	rejected the vote request; so it must have known about the lock.
	This is a problem in the Lock-Block caching algorithm.
	
Action:	A cluster dump may be required here to solve the problem.  Also,
        check all the places where VPNOV and VPNO are set for a possible
        problem.  This logic resides mostly in routines EVEOKR and EIEOKR.
>)				;Yes - stop now!
	ENDIF.
	TMNE VPNO,(T1)		;Was this vote request denied by anyone?
	IFSKP.			;No
	  MOVE T1,QBADDR	;Get Q-Block address back
	  RETSKP		;Return success
	ENDIF.
	MOVE T1,.VPAD1(T1)	;Get error code from Additional Data
	RET			;Return failure

	ENDSV.
	SUBTTL	Interface Routines for ENQ -- EQCNTS (Get lock usage counts)

;This  routine is called from ENQFN3 (ENQ% function 3) in order to get
;the number of Q-Blocks on the queue for a particular Lock-Block. This
;must be known in  order  to  determine  if  the  .ENQMA  function  is
;allowable. All remote systems must be checked to see if there are any
;Q-Blocks  queued  for  the  lock  which  is about to  have its access
;changed. If so, then the access change will not be  permitted  if  we
;are attempting to change from SHARED to EXCLUSIVE.
;
;	T1/ Q-Block address
;
;       CALL EQCNTS             ;(T1/T1)
;
;Returns +1: Always, with:
;            T1/ Number of remote waiters,,Number of remote lockers
;
;Preserves T2.

;	XSWAPCD

EQCNTS::SAVEAC <T2>		;Preserve T2
	MOVE T3,T1		;Save Q-Block address
	SETZM T1		;Assume no remote user now
	MOVE T2,ENQWRD		;Get the ENQ word for this process
	TXNN T2,EQ%ENA		;Is cluster-wide capability enabled?
	RETSKP			;No, so just return
	LOAD T2,ENQFLG,(T3)	;Get flags for Q-Block
	TXNN T2,EN.CLL		;Is this a cluster-wide request?
	RETSKP			;No, so just return
	LOAD T4,ENQLBP,(T3)	;Get Lock-Block address
	LOAD T2,ENQFLG,(T4)	;Get flags
	TXNE T2,EN.NOV		;Do we need to vote on this lock?
	RETSKP			;No, so just return
	MOVE T1,T4		;Reposition Lock-Block address
	CALL HDRFIL		;(T1/T1,T2)Fill in most of the VRQA header
	XMOVEI T2,VRQA		;Get address of VRQA
	MOVEI T3,.CNTS		;Get function code
	STOR T3,EBFTYP,(T2)	;Store in VRQA
	CALL ASK4IT		;()Send the VRQA to all systems
	 RETBAD (,<SETO T1,>)	;Failed due to lack of resources
 	XMOVEI T1,VRPA		;Get VRPA address
	MOVE T1,.VPAD1(T1)	;Get remote count word from Additional Data
	RET			;Return success
	SUBTTL	Interface Routines for ENQ -- EQRSTS (Get remote lock status)

;This routine is called from ENQCF0 (ENQC% function 0) in order to get
;the remote status of the lock. This information is used to return the
;correct cluster-wide status of the lock. All remote nodes are queried
;about their own status on the lock.
;
;	T1/ Hash value
;	T2/ Lock-Block address or zero
;	P1-P5 set up by call to VALREQ and HASH
;	Q1/ Address of lock request in user's space; set up by VALARG
;
;       CALL EQRSTS             ;(T1,T2,Q1,P1-P5/T1,T2,T3)
;
;Returns +1: Always, with the following:
;
;	T1/ Remote lock access (Shared=0, Exclusive=-1),,
;	    Greatest remote lock level
;	T3/ Remote job number of lock owner (-1 if unowned),,
;	    Number of remote users (lockers) of lock
;	T4/ Greatest remote time stamp (0 if lock was not known remotely)

;	XSWAPCD

EQRSTS::STKVAR <TMPHSH,TMPLBA>
	MOVEM T1,TMPHSH		;Save the hash value ...
	MOVEM T2,TMPLBA		;... and the Lock-Block address
	SETZB T1,T4		;Init the returned values
	HRLZI T3,-1
	HRRE T2,P1		;Get object type and extend sign
	SKIPL T2		;Is this a non-file lock?
	IFSKP.			;Yes
	  TRNN P1,77000		;Is this a 400000+job-number type of lock?
	  RET			;Yes, so return now
	ENDIF.
	MOVE T2,ENQWRD		;Get the ENQ word for this process
	TXNN T2,EQ%ENA		;Is cluster-wide capability enabled?
	RET			;No, so just return
	SKIPN T2,TMPLBA		;Was a Lock-Block specified?
	IFSKP.			;Yes
	  LOAD T2,ENQFLG,(T2)	;Get flags for Lock-Block
	  TXNE T2,EN.NOV	;Do we need to vote on this lock?
	  RET			;No, so just return
	ENDIF.
	MOVE T1,TMPHSH		;Get hash value back
	CALL HDRFIP		;[7.1103] (T1,Q1,P1-P5/T1,T2) Fill in header of VRQA
	XMOVEI T2,VRQA		;Get address of VRQA
	MOVEI T3,.RSTS		;Get opcode for vote
	STOR T3,EBFTYP,(T2)	;Put opcode into VRQA
	CALL ASK4IT		;()Get the remote status
	IFNSK.			;Failed due to lack of resources
	  SETZB T1,T4		;So, return "unknown remotely" values
	  HRLZI T3,-1
	  RET
	ENDIF.
	XMOVEI T2,VRPA		;Get address of VRPA
	SKIPE T4,.VPAD3(T2)	;Did we get any replies?
	IFSKP.			;No, remote time was zero so lock is unknown
	  SETZ T1,		;So, return "unknown remotely" values
	  HRLZI T3,-1
	ELSE.			;Yes, this was known remotely
	  HRRZ T3,.VPAD2(T2)	;Get the number of remote lockers
	  SETO T1,		;Assume no lock owner
	  SKIPE T3		;Are there any?
	  HLRZ T1,.VPAD2(T2)	;Yes, get the lock owners's job number
	  HRL T3,T1		;Put lock owner in correct position
	  MOVE T1,.VPAD1(T2)	;Get access,,level
	ENDIF.
	RET			;Done

	ENDSV.
	SUBTTL	Interface Routines for ENQ -- EQPOOL (Get pool total)

;This  routine  is  called  from  LOKSKD in order to get the remaining
;number of resources in the pool for a particular Lock-Block.
;
;	T1/ Address of Lock-Block being rescheduled
;
;       CALL EQPOOL             ;(T1/T1,T2)
;
;Returns +1: Always, with:
;            T2/ Total number of remaining resources in pool
;
;Preserves T1

;	XSWAPCD

EQPOOL::SAVEAC <T1,Q1>		;Preserve T1 and get a work register
	MOVEM T1,Q1		;Save Lock-Block address
	LOAD T2,ENQRR,(T1)	;Assume no need to vote
	MOVE T3,ENQWRD		;Get the ENQ word for this process
	TXNN T3,EQ%ENA		;Is cluster-wide capability enabled?
	RET			;No, so just return
	LOAD T3,ENQFLG,(T1)	;Get flags from Lock-Block
	TXNE T3,EN.CLL		;[7.1179] Is this a cluster-wide lock?
	TXNE T3,EN.NOV		;Do we need to vote on this lock?
	RET			;No, so just return
	CALL HDRFIL		;(T1/T1,T2)Fill in most of VRQA header
	XMOVEI T2,VRQA		;Get address of VRQA
	MOVEI T3,.POOL		;Get function code
	STOR T3,EBFTYP,(T2)	;Store in VRQA
	CALL ASK4IT		;()Send the VRQA to all systems
	 RETBAD (,<SETZ T2,>)	;Failed due to lack of resources
	XMOVEI T1,VRPA		;Get address of VRPA
	LOAD T3,ENQTR,(Q1)	;Get total number of resources for lock
	LOAD T2,ENQRR,(Q1)	;Get the number remaining
	SUB T3,T2		;Compute number used locally
	ADD T3,.VPAD1(T1)	;Add local amount to remote amount used
	LOAD T2,ENQTR,(Q1)	;Get total number of resources for lock
	SUB T2,T3		;Compute number remaining in cluster
	RET			;Done
	SUBTTL	Interface Routines for ENQ -- EQQSKD (QSKED remote)

;This  routine  is  called from QSKED in order to perform a scheduling
;pass  for a particular Q-Block  on all remote  nodes.  This  call  is
;made  after it has been determined that the scheduling pass succeeded
;locally (except for pool total checking). The  following  is  checked
;remotely:
;
;	1.  Mask Block compatibility
;	2.  Sharer groups
;
;	T1/ Address of Q-Block to be scheduled
;
;       CALL EQQSKD             ;(T1/T1,T4)
;
;Returns +1: Failure, Access disallowed by remote system(s)
;        +2: Success (No conflicts exist in the cluster), with:
;            T4/ Total number of remaining resources in pool for Lock-Block
;
;Preserves T1.

;	XSWAPCD

EQQSKD::SAVEAC <T1,Q1>		;Preserve T1 and get a work register
	LOAD Q1,ENQLBP,(T1)	;Get Lock-Block address
	LOAD T4,ENQRR,(Q1)	;Get the number of resources remaining locally
	MOVE T2,ENQWRD		;Get the ENQ word for this process
	TXNN T2,EQ%ENA		;Is cluster-wide capability enabled?
	RETSKP			;No, so just return
	LOAD T2,ENQFLG,(T1)	;Get flags for Q-Block
	TXNN T2,EN.CLL		;Is this a cluster-wide request?
	RETSKP			;No, so just return
	LOAD T2,ENQFLG,(Q1)	;Get flags from Lock-Block
	TXNE T2,EN.NOV		;Do we need to vote on this lock?
	RETSKP			;No, so just return
	CALL HDRFIQ		;(T1/T1,T2)Fill in most of the VRQA header
	XMOVEI T2,VRQA		;Get address of VRQA
	MOVEI T3,.QSKD		;Get function code
	STOR T3,EBFTYP,(T2)	;Store in VRQA
	CALL ASK4IT		;()Send the VRQA to all systems
	 RETBAD ()		;Failed due to lack of resources
	XMOVEI T1,VRPA		;Get address of VRPA
	TMNE VPNO,(T1)		;Was this vote request denied by anyone?
	RET			;Yes, so return failure
	LOAD T3,ENQTR,(Q1)	;Get total number of resources for lock
	LOAD T2,ENQRR,(Q1)	;Get the number remaining
	SUB T3,T2		;Compute number used locally
	ADD T3,.VPAD1(T1)	;Add local amount to remote amount used
	LOAD T4,ENQTR,(Q1)	;Get total number of resources for lock
	SUB T4,T3		;Compute number remaining in cluster
	RETSKP			;Done, return success
	SUBTTL	Interface Routines for ENQ -- EQLKSD (Notify for remote LOKSKD)

;This  routine  is  called  from  LOKSKD  in order to perform a remote
;scheduling pass for a particular Lock-Block. When the local node does
;a call to LOKSKD, this routine is called to notify the  remote  nodes
;of the event and cause a scheduling pass.
;
;	T1/ Address of Lock-Block to reschedule
;
;       CALL EQLKSD             ;(T1/T1)
;
;Returns +1: Always, with T1 preserved

;	XSWAPCD

EQLKSD::SAVEAC <T1>		;Preserve T1
	MOVE T2,ENQWRD		;Get the ENQ word for this process
	TXNN T2,EQ%ENA		;Is cluster-wide capability enabled?
	RET			;No, so just return
	LOAD T2,ENQFLG,(T1)	;Get flags from Lock-Block
	TXNE T2,EN.CLL		;[7.1179] Is this a cluster-wide lock?
	TXNE T2,EN.NOV		;Do we need to vote on this lock?
	RET			;No, so just return
	CALL HDRFIL		;(T1/T1,T2)Fill in most of the VRQA header
	XMOVEI T2,VRQA		;Get address of VRQA
	MOVEI T3,.LKSD		;Get function code
	STOR T3,EBFTYP,(T2)	;Store in VRQA
	CALL ASK4IT		;()Send the VRQA to all systems
	 BUG.(CHK,EQNOTF,ENQSRV,SOFT,<ENQSRV - Rescheduling notification failed>,,<

Cause:	Routine EQLKSD was called to notify the other systems in the cluster
	that a lock has been rescheduled.  But, this did not occur because
	of a lack of resources; SCA message buffers.  So, it may be that
	there is fork on another system which is hung waiting to get access
	to this lock and it won't unhang until that system recieves a 
	rescheduling notification.  This BUGCHK should not occur and is
	evidence of a much more serious SCA problem.
	
Action:	In order to unhang any forks which may be waiting for access to the
	lock which was just rescheduled, the following can be done.  On 
	every system, set bit EN.SDO in every Lock-Block which is on the 
	EQLBLT.  Then increment the right half of EQFKFL by the number of 
	blocks just set.  This can be done by setting EQCSTF to -1 on 
	each system.  This will make the system think that a cluster 
	state change occurred and will force a rescheduling of all known
	lock blocks.
>,,<DB%NND>)			;[7.1210][7.1096] Failed, lack of resources
	RET			;All done - systems have been notified
	SUBTTL End of ENQSRV

   >				;End of IFN CLEQIN at top of module

	TNXEND
	END