#pragma module SWDRIVER "X-1" /************************************************************************/ /* */ /* Copyright ) Digital Equipment Corporation, 1997 */ /* All Rights Reserved. */ /* Unpublished rights reserved under the copyright laws of the United */ /* States. */ /* */ /* The software contained on this media is proprietary to and embodies */ /* the confidential technology of Digital Equipment Corporation. */ /* Possession, use, duplication or dissemination of the software and */ /* media is authorized only pursuant to a valid written license from */ /* Digital Equipment Corporation. */ /* */ /* RESTRICTED RIGHTS LEGEND Use, duplication, or disclosure by the */ /* U.S. Government is subject to restrictions as set forth in */ /* Subparagraph (c)(1)(ii) of DFARS 252.227-7013, or in FAR 52.227-19, */ /* as applicable. */ /* */ /************************************************************************/ /************************************************************************/ /* */ /* Facility: */ /* Switching Pseudo Driver */ /* */ /* Abstract: */ /* This driver acts to switch I/O paths between several devices */ /* which are intended to all point at the same physical storage. */ /* It reuses a "prime" or "master" or "principal" UCB of the unit */ /* which it intercepts, and directs I/O either to that driver or */ /* to another unit which must first have been registered with it. */ /* The IO$_PHYSICAL function of THIS driver is used to control it, */ /* controlling the connection or disconnection of units to the */ /* tuples controlled here, controlling manual failover or failback */ /* (either when the unit is idle, or idling the device first and */ /* un-idling after), and the start_io entry is used to direct */ /* IRPs to underlying paths via the ioc$initiate route provided */ /* the underlying unit is not in Mnt Ver or its shadowing equiv. */ /* Apart from this the intercepted unit appears normal. */ /* In addition to facilities to control this driver via IO$_PHYSICAL*/ /* a routine to handle most all the same inputs is provided, */ /* pointed to by its DDT entry ddt$ps_mntver_2 (see routine */ /* fakeformat() below ). */ /* Author: */ /* Glenn C. Everhart */ /* */ /* */ /* Revision History: */ /* X-1 Glenn C. Everhart 10-Feb-1997 */ /* Initial version. */ /* */ /************************************************************************/ /* Build instructions: */ /* =================== */ /* */ /* $ CC_OPT = "/DEFINE=(BASEALIGN_SUPPORT)" */ /* $ IF P1 .NES. "" */ /* $ THEN */ /* $ IF P1 .NES. "DEBUG" THEN EXIT %X14 ! SS$_BADPARAM */ /* $ CC_OPT = "/DEBUG/NOOPTIMIZE/DEFINE=(DEBUG,BASEALIGN_SUPPORT)" */ /* $ ENDIF */ /* $ */ /* $! Compile the driver */ /* $ */ /* $ CC/STANDARD=RELAXED_ANSI89/INSTRUCTION=NOFLOATING_POINT - */ /* /EXTERN=STRICT 'CC_OPT' - */ /* /LIS=SWDRIVER/MACHINE_CODE/OBJ=SWDRIVER - */ /* SWDRIVER+SYS$LIBRARY:SYS$LIB_C.TLB/LIBRARY */ /* $ */ /* $! Link the driver */ /* $ */ /* $ LINK/ALPHA/USERLIB=PROC/NATIVE_ONLY/BPAGE=14/SECTION/REPLACE- */ /* /NODEMAND_ZERO/NOTRACEBACK/SYSEXE/NOSYSSHR- */ /* /SHARE=SYS$SWDRIVER- ! Driver image */ /* /DSF=SYS$SWDRIVER- ! Debug symbol file */ /* /SYMBOL=SYS$SWDRIVER- ! Symbol table */ /* /MAP=SYS$SWDRIVER/FULL/CROSS - ! Map listing */ /* SYS$INPUT:/OPTIONS */ /* SYMBOL_TABLE=GLOBALS */ /* CLUSTER=VMSDRIVER,,,- */ /* SWDRIVER.OBJ,- */ /* SYS$LIBRARY:VMS$VOLATILE_PRIVATE_INTERFACES/INCLUDE=(BUGCHECK_CODES)/LIB,- */ /* SYS$LIBRARY:STARLET/INCLUDE:(SYS$DRIVER_INIT,SYS$DOINIT) */ /* COLLECT=NONPAGED_EXECUTE_PSECTS/ATTRIBUTES=RESIDENT,- */ /* $CODE$ */ /* PSECT_ATTR=$LINK$,WRT */ /* PSECT_ATTR=$INITIAL$,WRT */ /* PSECT_ATTR=$LITERAL$,NOPIC,NOSHR,WRT */ /* PSECT_ATTR=$READONLY$,NOPIC,NOSHR,WRT */ /* PSECT_ATTR=$$$105_PROLOGUE,NOPIC */ /* PSECT_ATTR=$$$110_DATA,NOPIC */ /* PSECT_ATTR=$$$115_LINKAGE,WRT */ /* COLLECT=NONPAGED_READWRITE_PSECTS/ATTRIBUTES=RESIDENT,$PLIT$, - */ /* $INITIAL$,$GLOBAL$,$OWN$,$$$105_PROLOGUE,$$$110_DATA, */ /* $$$115_LINKAGE,$BSS$,$DATA$,$LINK$,$LITERAL$,$READONLY$ */ /* PSECT_ATTR=EXEC$INIT_CODE,NOSHR */ /* COLLECT=INITIALIZATION_PSECTS/ATTRIBUTES=INITIALIZATION_CODE,- */ /* EXEC$INIT_000, EXEC$INIT_001,EXEC$INIT_002,- */ /* EXEC$INIT_CODE,EXEC$INIT_LINKAGE,EXEC$INIT_SSTBL_000,- */ /* EXEC$INIT_SSTBL_001,EXEC$INIT_SSTBL_002 */ /* $QUIT: */ /* $ EXIT $STATUS */ /* Define system data structure types and constants */ /* This driver is able to handle an extended IRP which has a context stack. Until the definitions can be checked in, handle the IRP definition locally. */ #define STDIRP 0 /* for now, use non standard IRP def */ #define IRP_CTXSTKSIZ 64 /* size of context stack in longs */ #define IO$M_FMODIFIERS 0xFFC0 #define IO$V_FMODIFIERS 6 #define HYSTERESIS 400 #define UCB$M_HIDEUCB 0x80000000 #define UCB$V_HIDEUCB 31 #if STDIRP #include irpdef /* I/O request packet */ #endif /* Definition for build purposes of an IRP with the extra fields we need */ #if !STDIRP /*** MODULE $IRPDEF ***/ #ifndef __IRPDEF_LOADED #define __IRPDEF_LOADED 1 #pragma __nostandard /* This file uses non-ANSI-Standard features */ #pragma __member_alignment __save #pragma __nomember_alignment #ifdef __INITIAL_POINTER_SIZE /* Defined whenever ptr size pragmas supported */ #pragma __required_pointer_size __save /* Save the previously-defined required ptr size */ #pragma __required_pointer_size __short /* And set ptr size default to 32-bit pointers */ #endif #ifdef __cplusplus extern "C" { #define __unknown_params ... #define __optional_params ... #else #define __unknown_params #define __optional_params ... #endif #if !defined(__VAXC) #define __struct struct #define __union union #else #define __struct variant_struct #define __union variant_union #endif /*+ */ /* IRP - I/O REQUEST PACKET */ /* */ /* I/O REQUEST PACKETS ARE CONSTRUCTED BY THE QUEUE I/O REQUEST SYSTEM */ /* SERVICE. THE CONTENT OF AN I/O REQUEST PACKET DESCRIBES A FUNCTION TO */ /* BE PERFORMED ON A DEVICE UNIT. */ /* */ /* NOTE: Several fields of the IRP must be at the same offsets as their */ /* corresponding fields in the IRPE and CDRP. The equivalency of these */ /* offsets is verified by ASSUME statements in the [LIB]VFY_IRP_A_LIKES.MAR */ /* module. These ASSUMEs may need to be altered as well whenever an IRP */ /* field is removed or altered. */ /*- */ #include /* Define the DIOBM type; IRP contains an embedded DIOBM type */ #define IRP$M_WLE_REUSE 0x1 #define IRP$M_WLE_SUPWL 0x2 #define IRP$M_BUFIO 0x1 #define IRP$M_FUNC 0x2 #define IRP$M_PAGIO 0x4 #define IRP$M_COMPLX 0x8 #define IRP$M_VIRTUAL 0x10 #define IRP$M_CHAINED 0x20 #define IRP$M_SWAPIO 0x40 #define IRP$M_DIAGBUF 0x80 #define IRP$M_PHYSIO 0x100 #define IRP$M_TERMIO 0x200 #define IRP$M_MBXIO 0x400 #define IRP$M_EXTEND 0x800 #define IRP$M_FILACP 0x1000 #define IRP$M_MVIRP 0x2000 #define IRP$M_SRVIO 0x4000 #define IRP$M_CCB_LOOKED_UP 0x8000 #define IRP$M_CACHE_PAGIO 0x10000 #define IRP$M_FILL_BIT 0x20000 #define IRP$M_BUFOBJ 0x40000 #define IRP$M_TRUSTED 0x80000 #define IRP$M_FASTIO_DONE 0x100000 #define IRP$M_FASTIO 0x200000 #define IRP$M_FAST_FINISH 0x400000 #define IRP$M_DOPMS 0x800000 #define IRP$M_HIFORK 0x1000000 #define IRP$M_SRV_ABORT 0x2000000 #define IRP$M_LOCK_RELEASEABLE 0x4000000 #define IRP$M_DID_FAST_FDT 0x8000000 #define IRP$M_SYNCSTS 0x10000000 #define IRP$M_FINIPL8 0x20000000 #define IRP$M_START_PAST_HWM 0x1 #define IRP$M_END_PAST_HWM 0x2 #define IRP$M_ERASE 0x4 #define IRP$M_PART_HWM 0x8 #define IRP$M_LCKIO 0x10 #define IRP$M_SHDIO 0x20 #define IRP$M_CACHEIO 0x40 #define IRP$M_WLE 0x80 #define IRP$M_CACHE_SAFE 0x100 #define IRP$M_NOCACHE 0x200 #define IRP$M_ABORTIO 0x400 #define IRP$M_FORCEMV 0x800 #define IRP$M_HBRIO 0x1000 #define IRP$M_GOTSTK 0x2000 #define IRP$M_FCODE 0x3F #define IRP$K_CDRP 304 /* Offset to the CDRP within the IRP */ #define IRP$C_CDRP 304 /* Offset to the CDRP within the IRP */ #define IRP$M_PIO_ERROR 0x1 #define IRP$M_PIO_FANOUT 0x2 #define IRP$M_PIO_NOQUE 0x4 #define IRP$M_PIO_CANCEL 0x8 #define IRP$M_PIO_CTHRDOK 0x10 #define IRP$M_PIO_PHASEII 0x20 #define IRP$M_SHD_EXPEL_REMOVED 0x1 #define IRP$M_CLN_READY 0x1 #define IRP$M_CLN_DONE 0x2 #define IRP$M_CPY_FINI 0x4 #define IRP$K_BT_LEN 400 #define IRP$C_BT_LEN 400 #define IRP$K_CD_LEN 408 #define IRP$C_CD_LEN 408 #ifdef __cplusplus /* Define structure prototypes */ struct _wcb; struct _ucb; struct _ctxb; struct _shad; struct _hrb; struct _bufio; struct _irpe; struct _fdt_context; struct _arb; struct _kpb; struct _ccb; struct _fkb; struct _cdt; struct _cdrp; #endif /* #ifdef __cplusplus */ #if !defined(__NOBASEALIGN_SUPPORT) && !defined(__cplusplus) /* If using pre DECC V4.0 or C++ */ #pragma __nomember_alignment __quadword #else #pragma __nomember_alignment #endif typedef struct _irp { #pragma __nomember_alignment struct _irp *irp$l_ioqfl; /*I/O QUEUE FORWARD LINK */ struct _irp *irp$l_ioqbl; /*I/O QUEUE BACKWARD LINK */ unsigned short int irp$w_size; /*SIZE OF IRP IN BYTES */ unsigned char irp$b_type; /*STRUCTURE TYPE FOR IRP */ __union { unsigned char irp$b_rmod; /*ACCESS MODE OF REQUEST */ __struct { unsigned irp$v_mode : 2; /* MODE SUBFIELD */ unsigned irp$v_fill_22 : 6; } irp$r_rmod_bits; } irp$r_rmod_overlay; unsigned int irp$l_pid; /*PROCESS ID OF REQUESTING PROCESS */ #if !defined(__NOBASEALIGN_SUPPORT) && !defined(__cplusplus) /* If using pre DECC V4.0 or C++ */ #pragma __nomember_alignment __quadword #else #pragma __nomember_alignment #endif __union { __int64 irp$q_param_0; /*For PAGEFAULT and IOCIOPOST */ __struct { #pragma __nomember_alignment int irp$l_acb64x_offset; /* Offset to ACB64X structure embedded in this IRP */ char irp$b_fill_23 [4]; } irp$r_fill_1; } irp$r_fill_0; #if !defined(__NOBASEALIGN_SUPPORT) && !defined(__cplusplus) /* If using pre DECC V4.0 or C++ */ #pragma __nomember_alignment __quadword #else #pragma __nomember_alignment #endif __union { __int64 irp$q_param_1; /*For PAGEFAULT and IOCIOPOST */ __struct { #pragma __nomember_alignment unsigned int irp$l_acb_flags; /* ACB flags; valid only if ACB$M_FLAGS_VALID in RMOD set */ unsigned int irp$l_thread_pid; /* (Reserved for Kernel Threads) */ } irp$r_fill_3; } irp$r_fill_2; __union { struct _wcb *irp$l_wind; /*ADDRESS OF WINDOW BLOCK */ struct _irp *irp$l_mirp; /*LINK TO MASTER IRP */ void (*irp$l_kast)(); /*PIGGY BACK KERNEL AST ADDRESS */ } irp$r_wind_overlay; struct _ucb *irp$l_ucb; /*ADDRESS OF DEVICE UCB */ __union { #if !defined(__NOBASEALIGN_SUPPORT) && !defined(__cplusplus) /* If using pre DECC V4.0 or C++ */ #pragma __nomember_alignment __quadword #else #pragma __nomember_alignment #endif #ifdef __INITIAL_POINTER_SIZE /* Defined whenever ptr size pragmas supported */ #pragma __required_pointer_size __long /* And set ptr size default to 64-bit pointers */ void (*irp$pq_acb64_ast)(); /* 64-bit user AST routine address */ #else unsigned __int64 irp$pq_acb64_ast; #endif #pragma __nomember_alignment #ifdef __INITIAL_POINTER_SIZE /* Defined whenever ptr size pragmas supported */ #pragma __required_pointer_size __short /* And set ptr size default to 32-bit pointers */ #endif struct _irp *irp$l_shd_iofl; /* Link to clone IRPs */ struct _ctxb *irp$l_ctxb; /* Link to CTXB */ int irp$l_iirp_p0; /* Generic parameter cell in internal IRPs */ } irp$r_acb64_ast_overlay; __union { #if !defined(__NOBASEALIGN_SUPPORT) && !defined(__cplusplus) /* If using pre DECC V4.0 or C++ */ #pragma __nomember_alignment __quadword #else #pragma __nomember_alignment #endif unsigned __int64 irp$q_acb64_astprm; /* 64-bit user AST parameter value */ #pragma __nomember_alignment struct _shad *irp$l_shad; /* SHAD address */ struct _hrb *irp$l_hrb; /* HRB address */ int irp$l_mv_tmo; /* Timeout value in internal mount verification IRPs */ int irp$l_iirp_p1; /* Generic parameter cell in internal IRPs */ } irp$r_acb64_astprm_overlay; #if !defined(__NOBASEALIGN_SUPPORT) && !defined(__cplusplus) /* If using pre DECC V4.0 or C++ */ #pragma __nomember_alignment __quadword #else #pragma __nomember_alignment #endif unsigned __int64 irp$q_user_thread_id; /* Unique user thread identifier */ #pragma __nomember_alignment unsigned char irp$b_efn; /*EVENT FLAG NUMBER AND EVENT GROUP */ unsigned char irp$b_pri; /*BASE PRIORITY OF REQUESTING PROCESS */ unsigned char irp$b_cln_indx; /*Shadow Clone membership index */ __union { /* Write log flags. */ unsigned char irp$b_wlg_flags; /* These flags are shared by DUDRIVER and SHDRIVER and MSCP. */ __struct { /* Write log Flags Status Bits */ unsigned irp$v_wle_reuse : 1; /* Reuse writelog entry */ unsigned irp$v_wle_supwl : 1; /* Supplementary writelog */ unsigned irp$v_fill_24 : 6; } irp$r_wlg_flag_bits; /* */ } irp$r_wlg_flags_overlay; /* */ unsigned int irp$l_chan; /* Process I/O channel */ #if !defined(__NOBASEALIGN_SUPPORT) && !defined(__cplusplus) /* If using pre DECC V4.0 or C++ */ #pragma __nomember_alignment __quadword #else #pragma __nomember_alignment #endif __union { #ifdef __INITIAL_POINTER_SIZE /* Defined whenever ptr size pragmas supported */ #pragma __required_pointer_size __long /* And set ptr size default to 64-bit pointers */ void *irp$pq_iosb; /* 64-bit address of caller's IOSB */ #else unsigned __int64 irp$pq_iosb; #endif __struct { #pragma __nomember_alignment __union { unsigned int irp$l_cln_wle; /* write log entry */ __int64 irp$q_param_2; /* For PAGEFAULT and IOCIOPOST (Kthreads) */ int irp$l_iirp_p2; /* Generic parameter cell in internal IRPs */ } irp$r_iosb_overlay; } irp$r_fill_5; } irp$r_fill_4; #if !defined(__NOBASEALIGN_SUPPORT) && !defined(__cplusplus) /* If using pre DECC V4.0 or C++ */ #pragma __nomember_alignment __quadword #else #pragma __nomember_alignment #endif __union { unsigned __int64 irp$q_status; /*Big time REQUEST STATUS */ __struct { #pragma __nomember_alignment __union { unsigned int irp$l_sts; /* Status */ __struct { unsigned irp$v_bufio : 1; /* BUFFERED I/O FLAG ;THESE BITS */ unsigned irp$v_func : 1; /* 1=>READ FUNCTION ;MUST BE ADJACENT */ unsigned irp$v_pagio : 1; /* PAGING I/O FLAG ;AND IN ORDER */ unsigned irp$v_complx : 1; /* COMPLEX BUFFERED I/O */ unsigned irp$v_virtual : 1; /* VIRTUAL I/O FUNCTION */ unsigned irp$v_chained : 1; /* CHAINED BUFFERED I/O OPERATION */ unsigned irp$v_swapio : 1; /* SWAP I/O OPERATION */ unsigned irp$v_diagbuf : 1; /* DIAGNOSTIC BUFFER ALLOCATED */ unsigned irp$v_physio : 1; /* PHYSICAL I/O */ unsigned irp$v_termio : 1; /* TERMINAL I/O (FOR SELECTING PRIORITY INC) */ unsigned irp$v_mbxio : 1; /* MAILBOX BUFFERED READ */ unsigned irp$v_extend : 1; /* AN IRPE IS LINKED TO THIS IRP */ unsigned irp$v_filacp : 1; /* FILE ACP I/O (BOTH DIOCNT AND BIOCNT) */ unsigned irp$v_mvirp : 1; /* MOUNT VERIFICATION IRP */ unsigned irp$v_srvio : 1; /* SERVER TYPE I/O (TRIGGER MOUNTVER ON ERROR BUT DON'T STALL) */ unsigned irp$v_ccb_looked_up : 1; /* Set if IRP$PS_CCB contains valid CCB address */ unsigned irp$v_cache_pagio : 1; /* Cached page i/o */ unsigned irp$v_fill_bit : 1; /* Unused */ unsigned irp$v_bufobj : 1; /* Set if buffer object I/O */ unsigned irp$v_trusted : 1; /* Set if trusted Component I/O */ unsigned irp$v_fastio_done : 1; /* Set if this is an available Fast-IO IRP */ unsigned irp$v_fastio : 1; /* Set if IRP created by $IO_SETUP -- special delete action */ unsigned irp$v_fast_finish : 1; /* Set if IPL8 completion is expected */ unsigned irp$v_dopms : 1; /* =1 if this IRP should call PMS$ logging routines */ unsigned irp$v_hifork : 1; /* Device fork IPL > IPL$C_SCS */ unsigned irp$v_srv_abort : 1; /* Server I/O should be aborted */ unsigned irp$v_lock_releaseable : 1; /* Forklock can be released in favor of PM spinlock on Start I/O */ unsigned irp$v_did_fast_fdt : 1; /* Fast-IO may have locked buffers via standard FDT dispatch */ unsigned irp$v_syncsts : 1; /* VIOC can return SS$_SYNC on HIT if set. */ unsigned irp$v_finipl8 : 1; /* Finish at IPL8 hook */ unsigned irp$v_fill_25 : 2; } irp$r_fill_9; } irp$r_fill_8; __union { unsigned int irp$l_sts2; /* EXTENSION OF STATUS WORD */ __struct { unsigned irp$v_start_past_hwm : 1; /* I/O STARTS PAST HIGHWATER MARK */ unsigned irp$v_end_past_hwm : 1; /* I/O ENDS PAST HIGHWATER MARK */ unsigned irp$v_erase : 1; /* ERASE I/O FUNCTION */ unsigned irp$v_part_hwm : 1; /* PARTIAL HIGHWATER MARK UPDATE */ unsigned irp$v_lckio : 1; /* Locked I/O request (DECnet) */ unsigned irp$v_shdio : 1; /* This is a shadowing IRP */ unsigned irp$v_cacheio : 1; /* uses VBN cache buffers */ unsigned irp$v_wle : 1; /* I/O USES A WRITE LOG ENTRY */ unsigned irp$v_cache_safe : 1; /* this indicates that */ /* the request has been */ /* checked as regards */ /* caching. */ unsigned irp$v_nocache : 1; /* IO$M_NOVCACHE was */ /* set in QIO function */ unsigned irp$v_abortio : 1; /* set in EXE$ABORTIO */ unsigned irp$v_forcemv : 1; /* set to indicate forced MV in progress */ unsigned irp$v_hbrio : 1; /* This is a host based raid IRP. */ unsigned irp$v_gotstk : 1; unsigned irp$v_fill_26 : 2; } irp$r_fill_11; } irp$r_fill_10; } irp$r_fill_7; } irp$r_fill_6; #if !defined(__NOBASEALIGN_SUPPORT) && !defined(__cplusplus) /* If using pre DECC V4.0 or C++ */ #pragma __nomember_alignment __quadword #else #pragma __nomember_alignment #endif #ifdef __INITIAL_POINTER_SIZE /* Defined whenever ptr size pragmas supported */ #pragma __required_pointer_size __long /* And set ptr size default to 64-bit pointers */ struct _pte *irp$pq_va_pte; /* 64-bit process virtual addr of PTE */ #else unsigned __int64 irp$pq_va_pte; #endif #pragma __nomember_alignment __union { #ifdef __INITIAL_POINTER_SIZE /* Defined whenever ptr size pragmas supported */ #pragma __required_pointer_size __short /* And set ptr size default to 32-bit pointers */ #endif void *irp$l_svapte; /* 32-bit S0/S1 address of first PTE */ struct _bufio *irp$ps_bufio_pkt; /* Pointer to buffered I/O packet */ } irp$r_svapte_overlay; unsigned int irp$l_bcnt; /*BYTE COUNT OF TRANSFER */ unsigned int irp$l_boff; /* Byte offset */ __union { unsigned int irp$l_oboff; /* Original BOFF, for segmented DIO */ unsigned int irp$l_aboff; /* "Ambient" BOFF, for NETDRIVER */ } irp$r_oboff_overlay; struct _irpe *irp$l_extend; /* ADDRESS OF IRPE */ struct _fdt_context *irp$ps_fdt_context; /* Contains addr of the FDT Context structure */ #if !defined(__NOBASEALIGN_SUPPORT) && !defined(__cplusplus) /* If using pre DECC V4.0 or C++ */ #pragma __nomember_alignment __quadword #else #pragma __nomember_alignment #endif DIOBM irp$r_diobm; /* Embedded DIOBM to handle cross-process 32-bit PTE access */ #pragma __nomember_alignment __union { unsigned int irp$l_iost1; /*FIRST I/O STATUS LONGWORD (FOR I/O POST) */ int irp$l_media; /*MEDIA ADDRESS */ } irp$r_iost1_overlay; __union { unsigned int irp$l_iost2; /*SECOND I/O STATUS LONGWORD */ __union { int irp$l_tt_term; /*ADDRESS OF READ TERMINATORS MASK */ unsigned char irp$b_carcon; /*CARRIAGE CONTROL */ } irp$r_tt_term_overlay; __union { unsigned short int irp$w_shd_copy_type; /*TYPE OF COPY TO PERFORM */ __struct { unsigned short int irp$w_shd_vun; /* VIRTUAL UNIT NUMBER */ __union { unsigned short int irp$w_shd_dev_type; /*DEVICE TYPE */ unsigned short int irp$w_shd_mscp_disk_modifier; /*FIELD FOR MODIFIERS */ } irp$r_shd_iost2_inner; } irp$r_shd_iost2_stuff; } irp$r_shd_iost2_overlay; } irp$r_iost2_overlay; __union { unsigned __int64 irp$q_nt_prvmsk; /* PRIVILEGE MASK FOR DECNET */ unsigned __int64 irp$q_station; /* STATION FIELD FOR DECNET DRIVERS */ __union { unsigned __int64 irp$q_tt_state; /* TERMINAL STATE DEFINITIONS */ __struct { unsigned int irp$l_abcnt; /* ACCUMULATED BYTES TRANSFERED */ unsigned int irp$l_obcnt; /* ORIGINAL TRANSFER BYTE COUNT */ } irp$r_tt_state_fields; } irp$r_tt_state_overlay; } irp$r_nt_prvmsk_overlay; __union { unsigned int irp$l_func; /* I/O function code */ __struct { unsigned irp$v_fcode : 6; /* FUNCTION CODE FIELD */ unsigned irp$v_fmod : 10; /* FUNCTION MODIFIER FIELD */ } irp$r_func_bits; } irp$r_func_overlay; unsigned int irp$l_segvbn; /* VIRTUAL BLOCK NUMBER OF CURRENT SEGMENT */ __union { void *irp$l_diagbuf; /* DIAGNOSTIC BUFFER ADDRESS */ void *irp$l_scb_buf; /* SCB BUFFER ADDRESS */ unsigned short int irp$w_tt_prmpt; /* PROMPT SIZE */ } irp$r_diagbuf_overlay; __union { unsigned int irp$l_seqnum; /* SEQUENCE NUMBER */ struct _ucb *irp$l_dcd_src_ucb; /* DISK COPY DATA SOURCE UCB */ } irp$r_seqnum_overlay; struct _arb *irp$l_arb; /* ACCESS RIGHTS BLOCK ADDRESS */ __union { void *irp$l_keydesc; /* ADDRESS OF ENCRYPTION DESCRIPTOR */ unsigned int irp$l_wle_ptr; /* Clone Write log index */ unsigned char irp$b_cpy_mode; /* Copy mode identifier */ } irp$r_keydesc_overlay; struct _kpb *irp$ps_kpb; /* Pointer to KP block */ struct _ccb *irp$ps_ccb; /* Pointer to CCB for this I/O */ #if !defined(__NOBASEALIGN_SUPPORT) && !defined(__cplusplus) /* If using pre DECC V4.0 or C++ */ #pragma __nomember_alignment __quadword #else #pragma __nomember_alignment #endif __union { __int64 irp$q_qio_p1; /* QIO argument #1 (64-bits) */ __struct { #pragma __nomember_alignment int irp$l_qio_p1; /* (low-order 32-bit) */ char irp$b_fill_27 [4]; } irp$r_fill_13; } irp$r_fill_12; #if !defined(__NOBASEALIGN_SUPPORT) && !defined(__cplusplus) /* If using pre DECC V4.0 or C++ */ #pragma __nomember_alignment __quadword #else #pragma __nomember_alignment #endif __union { __int64 irp$q_qio_p2; /* QIO argument #2 (64-bits) */ __struct { #pragma __nomember_alignment int irp$l_qio_p2; /* (low-order 32-bit) */ char irp$b_fill_28 [4]; } irp$r_fill_15; } irp$r_fill_14; #if !defined(__NOBASEALIGN_SUPPORT) && !defined(__cplusplus) /* If using pre DECC V4.0 or C++ */ #pragma __nomember_alignment __quadword #else #pragma __nomember_alignment #endif __union { #pragma __nomember_alignment __int64 irp$q_qio_p3; /* QIO argument #3 (64-bits) */ int irp$l_qio_p3; /* (low-order 32-bit) */ __int64 irp$q_param_3; /* (for PAGEFAULT and IOCIOPOST) */ } irp$r_qio_p3_overlay; #if !defined(__NOBASEALIGN_SUPPORT) && !defined(__cplusplus) /* If using pre DECC V4.0 or C++ */ #pragma __nomember_alignment __quadword #else #pragma __nomember_alignment #endif __union { __int64 irp$q_qio_p4; /* QIO argument #4 (64-bits) */ __struct { #pragma __nomember_alignment int irp$l_qio_p4; /* (low-order 32-bit) */ char irp$b_fill_29 [4]; } irp$r_fill_17; } irp$r_fill_16; #if !defined(__NOBASEALIGN_SUPPORT) && !defined(__cplusplus) /* If using pre DECC V4.0 or C++ */ #pragma __nomember_alignment __quadword #else #pragma __nomember_alignment #endif __union { __int64 irp$q_qio_p5; /* QIO argument #5 (64-bits) */ __struct { #pragma __nomember_alignment int irp$l_qio_p5; /* (low-order 32-bit) */ char irp$b_fill_30 [4]; } irp$r_fill_19; } irp$r_fill_18; #if !defined(__NOBASEALIGN_SUPPORT) && !defined(__cplusplus) /* If using pre DECC V4.0 or C++ */ #pragma __nomember_alignment __quadword #else #pragma __nomember_alignment #endif __union { __int64 irp$q_qio_p6; /* QIO argument #6 (64-bits) */ __struct { #pragma __nomember_alignment int irp$l_qio_p6; /* (low-order 32-bit) */ char irp$b_fill_31 [4]; } irp$r_fill_21; } irp$r_fill_20; /* ALL FIELDS INSERTED ABOVE THIS POINT IN THE IRP */ /* MUST BE CHANGED IN THE CDRPDEF.SDL FILE. */ /* Standard IRP must contain space for Class Driver CDRP fields. */ struct _fkb *irp$l_fqfl; /* Fork Queue FLINK */ struct _fkb *irp$l_fqbl; /* Fork Queue Blink */ unsigned short int irp$w_cdrpsize; /* Size field for positive section only */ unsigned char irp$b_cd_type; /* Type, always of interest */ unsigned char irp$b_flck; /* Fork Lock number */ void (*irp$l_fpc)(); /* Fork PC */ __int64 irp$q_fr3; /* Fork R3 */ __int64 irp$q_fr4; /* Fork R4 */ void (*irp$l_savd_rtn)(); /* Saved return address from level 1 JSB */ void *irp$l_msg_buf; /* Address of allocated MSCP buffer */ unsigned int irp$l_rspid; /* Allocated Request ID */ struct _cdt *irp$l_cdt; /* Address of Connection Descriptor Table */ unsigned __int64 irp$q_res_wait_state; /* SCS Resource Wait State */ int irp$l_scs_stall_data; /* Data cell used by SCS to save data over a stall */ void *irp$l_rwcptr; /* RWAITCNT pointer */ void *irp$l_bd_addr; /* Address of Buffer Descriptor that maps I/O buffer */ void *irp$l_rbun; /* Address of Resource Bundle */ void *irp$l_lbufh_ad; /* Local BUFfer Handle ADress */ char irp$b_fill_32 [4]; /* Extensions to the CDRP within the IRP */ __union { /* Host-Based Shadowing Extension */ __struct { unsigned char irp$b_shd_pio_cnt; /* Tot num phys IRPs assoc. */ unsigned char irp$b_shd_pio_act; /* Tot num phys IRPs active. */ /* Note Keep SHD_PIO_FLAGS, SHD_PIO_ERRCNT, contiguous. */ __union { unsigned char irp$b_shd_pio_flags; /* Master Flags Byte */ __struct { unsigned irp$v_pio_error : 1; /* Errant clone in Chain */ unsigned irp$v_pio_fanout : 1; /* Chained Clones. */ unsigned irp$v_pio_noque : 1; /* Don't queue to server */ unsigned irp$v_pio_cancel : 1; /* This master cancelled */ unsigned irp$v_pio_cthrdok : 1; /* Copy thread validated. */ unsigned irp$v_pio_phaseii : 1; /* Bi-phasic Phase II write */ unsigned irp$v_fill_33 : 2; } irp$r_pio_bits; } irp$r_pio_flags_overlay; unsigned char irp$b_shd_pio_errcnt; /* Number of errors in chain */ unsigned char irp$b_shd_pio_errindex; /* Index of erring device */ unsigned char irp$b_shd_pio_errsev; /* Relative error severity */ short int irp$w_shd_filler; unsigned __int64 irp$q_shd_lock_fr0; /* Lock fork R0 */ unsigned __int64 irp$q_shd_lock_fr1; /* Lock fork R1 */ unsigned __int64 irp$q_shd_lock_fr2; /* Lock fork R2 */ unsigned __int64 irp$q_shd_lock_fr4; /* Lock fork R4 */ unsigned __int64 irp$q_shd_lock_fr5; /* Lock fork R5 */ void (*irp$l_shd_lock_fpc)(); /* Lock fork PC */ unsigned int irp$l_shd_pio_error; /* BCNT and Error Status (SS$_) */ struct _irp *irp$l_shd_pio_lnk; /* Link to clone IRP(s) */ int (*irp$l_shdspc)(); /* Shadowing return PC */ struct _irp *irp$l_shd_control_irp; /* address of control IRP */ int irp$l_shd_temp; /* used for temporary storage */ unsigned __int64 irp$q_shd_saved_r1; /* second save area for WLG */ unsigned __int64 irp$q_shd_saved_r2; unsigned __int64 irp$q_shd_saved_r4; unsigned int irp$l_shd_svd_cnt_irp; /* save SHD_CONTROL_IRP */ unsigned int irp$l_shd_saved_status; /* save area for status */ unsigned int irp$l_shd_wlg_mode_fpc; /* saved PC for WLG_MODE fork */ unsigned int irp$l_shd_perlkid; /* holds sublock id for */ /* per-disk */ unsigned int irp$l_shd_expel_timer; /* Clone error timer */ __union { unsigned int irp$l_shd_expel_flags; /* Clone IRP flags */ __struct { unsigned irp$v_shd_expel_removed : 1; /* Device is expelled */ unsigned irp$v_fill_34 : 7; } irp$r_expel_bits; } irp$r_expel_flags_overlay; unsigned int irp$l_shd_expel_mask; /* indicate units to be expelled in MIRP */ unsigned __int64 irp$q_shd_reserv_q8; /* will be needed for 64-bit saves */ unsigned __int64 irp$q_shd_reserv_q9; /* will be needed for 64-bit saves */ unsigned __int64 irp$q_shd_reserv_q10; /* will be needed for 64-bit saves */ __union { unsigned char irp$b_shd_flags; /* Shadow Clone Flags */ __struct { /* Clone Flags Status Bits */ unsigned irp$v_cln_ready : 1; /* Clone is ready for I/O */ unsigned irp$v_cln_done : 1; /* Clone has done I/O */ unsigned irp$v_cpy_fini : 1; /* Copy is complete. */ unsigned irp$v_fill_35 : 5; } irp$r_shd_flag_bits; /* */ } irp$r_shd_flags_overlay; unsigned long irp$l_curcspx; /* Current context stack pointer */ long irp$l_stkflgsx; /* struct stkfs {*/ /* unsigned irp$v_onstackx : 1;*/ /* current data is on the ctx stk */ /* unsigned irp$v_csfil : 31; */ /* } irp$l_stkflgsx; */ unsigned long irp$a_ctxstkx[IRP_CTXSTKSIZ]; /* */ } irp$r_shadowing_extension; /* Block Transfer Extension */ __struct { unsigned int irp$l_lboff; /* Local Byte OFFset */ __union { void *irp$l_rbufh_ad; /* Remote BUFfer Handle ADress */ struct _cdrp *irp$l_cdrpfl; } irp$r_rbufh_ad_overlay; unsigned int irp$l_rboff; /* Remote Byte OFFset */ unsigned int irp$l_xct_len; /* Transfer length in bytes */ } irp$r_blk_xfer_extension; /* Class Driver Extension */ __struct { char irp$t_lbufhndl [12]; /* Local buffer handle */ unsigned int irp$l_ubarsrce; /* Scratch Cell used for DU/TUDRIVER convenience */ unsigned int irp$l_dutuflags; /* Class driver status flags: */ unsigned short int irp$w_dutucntr; /* General purpose counter */ unsigned short int irp$w_endmsgsiz; /* Size of most recent MSCP end message */ } irp$r_cls_drv_extension; /* File system extensions */ unsigned int irp$l_erase_vbn; /* VBN to start HWM erase */ } irp$r_cdrp_extensions; char irp$b_fill_36 [3]; } IRP; #define irp$b_rmod irp$r_rmod_overlay.irp$b_rmod #define irp$v_mode irp$r_rmod_overlay.irp$r_rmod_bits.irp$v_mode #define irp$q_param_0 irp$r_fill_0.irp$q_param_0 #define irp$l_acb64x_offset irp$r_fill_0.irp$r_fill_1.irp$l_acb64x_offset #define irp$q_param_1 irp$r_fill_2.irp$q_param_1 #define irp$l_acb_flags irp$r_fill_2.irp$r_fill_3.irp$l_acb_flags #define irp$l_thread_pid irp$r_fill_2.irp$r_fill_3.irp$l_thread_pid #define irp$l_wind irp$r_wind_overlay.irp$l_wind #define irp$l_mirp irp$r_wind_overlay.irp$l_mirp #define irp$l_kast irp$r_wind_overlay.irp$l_kast #define irp$pq_acb64_ast irp$r_acb64_ast_overlay.irp$pq_acb64_ast #define irp$l_shd_iofl irp$r_acb64_ast_overlay.irp$l_shd_iofl #define irp$l_ctxb irp$r_acb64_ast_overlay.irp$l_ctxb #define irp$l_iirp_p0 irp$r_acb64_ast_overlay.irp$l_iirp_p0 #define irp$q_acb64_astprm irp$r_acb64_astprm_overlay.irp$q_acb64_astprm #define irp$l_shad irp$r_acb64_astprm_overlay.irp$l_shad #define irp$l_hrb irp$r_acb64_astprm_overlay.irp$l_hrb #define irp$l_mv_tmo irp$r_acb64_astprm_overlay.irp$l_mv_tmo #define irp$l_iirp_p1 irp$r_acb64_astprm_overlay.irp$l_iirp_p1 #define irp$b_wlg_flags irp$r_wlg_flags_overlay.irp$b_wlg_flags #define irp$v_wle_reuse irp$r_wlg_flags_overlay.irp$r_wlg_flag_bits.irp$v_wle_reuse #define irp$v_wle_supwl irp$r_wlg_flags_overlay.irp$r_wlg_flag_bits.irp$v_wle_supwl #define irp$pq_iosb irp$r_fill_4.irp$pq_iosb #define irp$l_cln_wle irp$r_fill_4.irp$r_fill_5.irp$r_iosb_overlay.irp$l_cln_wle #define irp$q_param_2 irp$r_fill_4.irp$r_fill_5.irp$r_iosb_overlay.irp$q_param_2 #define irp$l_iirp_p2 irp$r_fill_4.irp$r_fill_5.irp$r_iosb_overlay.irp$l_iirp_p2 #define irp$q_status irp$r_fill_6.irp$q_status #define irp$l_sts irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$l_sts #define irp$v_bufio irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_bufio #define irp$v_func irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_func #define irp$v_pagio irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_pagio #define irp$v_complx irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_complx #define irp$v_virtual irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_virtual #define irp$v_chained irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_chained #define irp$v_swapio irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_swapio #define irp$v_diagbuf irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_diagbuf #define irp$v_physio irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_physio #define irp$v_termio irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_termio #define irp$v_mbxio irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_mbxio #define irp$v_extend irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_extend #define irp$v_filacp irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_filacp #define irp$v_mvirp irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_mvirp #define irp$v_srvio irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_srvio #define irp$v_ccb_looked_up irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_ccb_looked_up #define irp$v_cache_pagio irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_cache_pagio #define irp$v_fill_bit irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_fill_bit #define irp$v_bufobj irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_bufobj #define irp$v_trusted irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_trusted #define irp$v_fastio_done irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_fastio_done #define irp$v_fastio irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_fastio #define irp$v_fast_finish irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_fast_finish #define irp$v_dopms irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_dopms #define irp$v_hifork irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_hifork #define irp$v_srv_abort irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_srv_abort #define irp$v_lock_releaseable irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_lock_releaseable #define irp$v_did_fast_fdt irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_did_fast_fdt #define irp$v_syncsts irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_syncsts #define irp$v_finipl8 irp$r_fill_6.irp$r_fill_7.irp$r_fill_8.irp$r_fill_9.irp$v_finipl8 #define irp$l_sts2 irp$r_fill_6.irp$r_fill_7.irp$r_fill_10.irp$l_sts2 #define irp$v_start_past_hwm irp$r_fill_6.irp$r_fill_7.irp$r_fill_10.irp$r_fill_11.irp$v_start_past_hwm #define irp$v_end_past_hwm irp$r_fill_6.irp$r_fill_7.irp$r_fill_10.irp$r_fill_11.irp$v_end_past_hwm #define irp$v_erase irp$r_fill_6.irp$r_fill_7.irp$r_fill_10.irp$r_fill_11.irp$v_erase #define irp$v_part_hwm irp$r_fill_6.irp$r_fill_7.irp$r_fill_10.irp$r_fill_11.irp$v_part_hwm #define irp$v_lckio irp$r_fill_6.irp$r_fill_7.irp$r_fill_10.irp$r_fill_11.irp$v_lckio #define irp$v_shdio irp$r_fill_6.irp$r_fill_7.irp$r_fill_10.irp$r_fill_11.irp$v_shdio #define irp$v_cacheio irp$r_fill_6.irp$r_fill_7.irp$r_fill_10.irp$r_fill_11.irp$v_cacheio #define irp$v_wle irp$r_fill_6.irp$r_fill_7.irp$r_fill_10.irp$r_fill_11.irp$v_wle #define irp$v_cache_safe irp$r_fill_6.irp$r_fill_7.irp$r_fill_10.irp$r_fill_11.irp$v_cache_safe #define irp$v_nocache irp$r_fill_6.irp$r_fill_7.irp$r_fill_10.irp$r_fill_11.irp$v_nocache #define irp$v_abortio irp$r_fill_6.irp$r_fill_7.irp$r_fill_10.irp$r_fill_11.irp$v_abortio #define irp$v_forcemv irp$r_fill_6.irp$r_fill_7.irp$r_fill_10.irp$r_fill_11.irp$v_forcemv #define irp$v_hbrio irp$r_fill_6.irp$r_fill_7.irp$r_fill_10.irp$r_fill_11.irp$v_hbrio #define irp$v_gotstk irp$r_fill_6.irp$r_fill_7.irp$r_fill_10.irp$r_fill_11.irp$v_gotstk #define irp$l_svapte irp$r_svapte_overlay.irp$l_svapte #define irp$ps_bufio_pkt irp$r_svapte_overlay.irp$ps_bufio_pkt #define irp$l_oboff irp$r_oboff_overlay.irp$l_oboff #define irp$l_aboff irp$r_oboff_overlay.irp$l_aboff #define irp$l_iost1 irp$r_iost1_overlay.irp$l_iost1 #define irp$l_media irp$r_iost1_overlay.irp$l_media #define irp$l_iost2 irp$r_iost2_overlay.irp$l_iost2 #define irp$l_tt_term irp$r_iost2_overlay.irp$r_tt_term_overlay.irp$l_tt_term #define irp$b_carcon irp$r_iost2_overlay.irp$r_tt_term_overlay.irp$b_carcon #define irp$w_shd_copy_type irp$r_iost2_overlay.irp$r_shd_iost2_overlay.irp$w_shd_copy_type #define irp$w_shd_vun irp$r_iost2_overlay.irp$r_shd_iost2_overlay.irp$r_shd_iost2_stuff.irp$w_shd_vun #define irp$w_shd_dev_type irp$r_iost2_overlay.irp$r_shd_iost2_overlay.irp$r_shd_iost2_stuff.irp$r_shd_iost2_inner.irp$w_shd_dev_ty\ pe #define irp$w_shd_mscp_disk_modifier irp$r_iost2_overlay.irp$r_shd_iost2_overlay.irp$r_shd_iost2_stuff.irp$r_shd_iost2_inner.irp$w_\ shd_mscp_disk_modifier #define irp$q_nt_prvmsk irp$r_nt_prvmsk_overlay.irp$q_nt_prvmsk #define irp$q_station irp$r_nt_prvmsk_overlay.irp$q_station #define irp$q_tt_state irp$r_nt_prvmsk_overlay.irp$r_tt_state_overlay.irp$q_tt_state #define irp$l_abcnt irp$r_nt_prvmsk_overlay.irp$r_tt_state_overlay.irp$r_tt_state_fields.irp$l_abcnt #define irp$l_obcnt irp$r_nt_prvmsk_overlay.irp$r_tt_state_overlay.irp$r_tt_state_fields.irp$l_obcnt #define irp$l_func irp$r_func_overlay.irp$l_func #define irp$v_fcode irp$r_func_overlay.irp$r_func_bits.irp$v_fcode #define irp$v_fmod irp$r_func_overlay.irp$r_func_bits.irp$v_fmod #define irp$l_diagbuf irp$r_diagbuf_overlay.irp$l_diagbuf #define irp$l_scb_buf irp$r_diagbuf_overlay.irp$l_scb_buf #define irp$w_tt_prmpt irp$r_diagbuf_overlay.irp$w_tt_prmpt #define irp$l_seqnum irp$r_seqnum_overlay.irp$l_seqnum #define irp$l_dcd_src_ucb irp$r_seqnum_overlay.irp$l_dcd_src_ucb #define irp$l_keydesc irp$r_keydesc_overlay.irp$l_keydesc #define irp$l_wle_ptr irp$r_keydesc_overlay.irp$l_wle_ptr #define irp$b_cpy_mode irp$r_keydesc_overlay.irp$b_cpy_mode #define irp$q_qio_p1 irp$r_fill_12.irp$q_qio_p1 #define irp$l_qio_p1 irp$r_fill_12.irp$r_fill_13.irp$l_qio_p1 #define irp$q_qio_p2 irp$r_fill_14.irp$q_qio_p2 #define irp$l_qio_p2 irp$r_fill_14.irp$r_fill_15.irp$l_qio_p2 #define irp$q_qio_p3 irp$r_qio_p3_overlay.irp$q_qio_p3 #define irp$l_qio_p3 irp$r_qio_p3_overlay.irp$l_qio_p3 #define irp$q_param_3 irp$r_qio_p3_overlay.irp$q_param_3 #define irp$q_qio_p4 irp$r_fill_16.irp$q_qio_p4 #define irp$l_qio_p4 irp$r_fill_16.irp$r_fill_17.irp$l_qio_p4 #define irp$q_qio_p5 irp$r_fill_18.irp$q_qio_p5 #define irp$l_qio_p5 irp$r_fill_18.irp$r_fill_19.irp$l_qio_p5 #define irp$q_qio_p6 irp$r_fill_20.irp$q_qio_p6 #define irp$l_qio_p6 irp$r_fill_20.irp$r_fill_21.irp$l_qio_p6 #define irp$b_shd_pio_cnt irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$b_shd_pio_cnt #define irp$b_shd_pio_act irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$b_shd_pio_act #define irp$b_shd_pio_flags irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$r_pio_flags_overlay.irp$b_shd_pio_flags #define irp$v_pio_error irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$r_pio_flags_overlay.irp$r_pio_bits.irp$v_pio_error #define irp$v_pio_fanout irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$r_pio_flags_overlay.irp$r_pio_bits.irp$v_pio_fanout #define irp$v_pio_noque irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$r_pio_flags_overlay.irp$r_pio_bits.irp$v_pio_noque #define irp$v_pio_cancel irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$r_pio_flags_overlay.irp$r_pio_bits.irp$v_pio_cancel #define irp$v_pio_cthrdok irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$r_pio_flags_overlay.irp$r_pio_bits.irp$v_pio_cthrdok #define irp$v_pio_phaseii irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$r_pio_flags_overlay.irp$r_pio_bits.irp$v_pio_phaseii #define irp$b_shd_pio_errcnt irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$b_shd_pio_errcnt #define irp$b_shd_pio_errindex irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$b_shd_pio_errindex #define irp$b_shd_pio_errsev irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$b_shd_pio_errsev #define irp$q_shd_lock_fr0 irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$q_shd_lock_fr0 #define irp$q_shd_lock_fr1 irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$q_shd_lock_fr1 #define irp$q_shd_lock_fr2 irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$q_shd_lock_fr2 #define irp$q_shd_lock_fr4 irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$q_shd_lock_fr4 #define irp$q_shd_lock_fr5 irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$q_shd_lock_fr5 #define irp$l_shd_lock_fpc irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$l_shd_lock_fpc #define irp$l_shd_pio_error irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$l_shd_pio_error #define irp$l_shd_pio_lnk irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$l_shd_pio_lnk #define irp$l_shdspc irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$l_shdspc #define irp$l_shd_control_irp irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$l_shd_control_irp #define irp$l_shd_temp irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$l_shd_temp #define irp$q_shd_saved_r1 irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$q_shd_saved_r1 #define irp$q_shd_saved_r2 irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$q_shd_saved_r2 #define irp$q_shd_saved_r4 irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$q_shd_saved_r4 #define irp$l_shd_svd_cnt_irp irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$l_shd_svd_cnt_irp #define irp$l_shd_saved_status irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$l_shd_saved_status #define irp$l_shd_wlg_mode_fpc irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$l_shd_wlg_mode_fpc #define irp$l_shd_perlkid irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$l_shd_perlkid #define irp$l_shd_expel_timer irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$l_shd_expel_timer #define irp$l_shd_expel_flags irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$r_expel_flags_overlay.irp$l_shd_expel_flags #define irp$v_shd_expel_removed irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$r_expel_flags_overlay.irp$r_expel_bits.irp$v_sh\ d_expel_removed #define irp$l_shd_expel_mask irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$l_shd_expel_mask #define irp$q_shd_reserv_q8 irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$q_shd_reserv_q8 #define irp$q_shd_reserv_q9 irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$q_shd_reserv_q9 #define irp$q_shd_reserv_q10 irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$q_shd_reserv_q10 #define irp$b_shd_flags irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$r_shd_flags_overlay.irp$b_shd_flags #define irp$v_cln_ready irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$r_shd_flags_overlay.irp$r_shd_flag_bits.irp$v_cln_ready #define irp$v_cln_done irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$r_shd_flags_overlay.irp$r_shd_flag_bits.irp$v_cln_done #define irp$v_cpy_fini irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$r_shd_flags_overlay.irp$r_shd_flag_bits.irp$v_cpy_fini #define irp$l_curcsp irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$l_curcspx #define irp$v_onstack (irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$l_stkflgsx & 1) #define irp$a_ctxstk irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$a_ctxstkx[0] #define irp$l_stkflgs irp$r_cdrp_extensions.irp$r_shadowing_extension.irp$l_stkflgsx #define irp$l_cdrpfl irp$r_cdrp_extensions.irp$r_blk_xfer_extension.irp$r_rbufh_ad_overlay.irp$l_cdrpfl #define irp$l_rboff irp$r_cdrp_extensions.irp$r_blk_xfer_extension.irp$l_rboff #define irp$l_xct_len irp$r_cdrp_extensions.irp$r_blk_xfer_extension.irp$l_xct_len #define irp$t_lbufhndl irp$r_cdrp_extensions.irp$r_cls_drv_extension.irp$t_lbufhndl #define irp$l_ubarsrce irp$r_cdrp_extensions.irp$r_cls_drv_extension.irp$l_ubarsrce #define irp$l_dutuflags irp$r_cdrp_extensions.irp$r_cls_drv_extension.irp$l_dutuflags #define irp$w_dutucntr irp$r_cdrp_extensions.irp$r_cls_drv_extension.irp$w_dutucntr #define irp$w_endmsgsiz irp$r_cdrp_extensions.irp$r_cls_drv_extension.irp$w_endmsgsiz #define irp$l_erase_vbn irp$r_cdrp_extensions.irp$l_erase_vbn #define IRP$K_LENGTH 744 /* LENGTH OF STANDARD IRP */ #define IRP$C_LENGTH 744 /* LENGTH OF STANDARD IRP */ #define IRP$S_IRPDEF 744 /* OLD IRP SIZE FOR COMPATIBILITY */ #pragma __member_alignment __restore #ifdef __INITIAL_POINTER_SIZE /* Defined whenever ptr size pragmas supported */ #pragma __required_pointer_size __restore /* Restore the previously-defined required ptr size */ #endif #ifdef __cplusplus } #endif #pragma __standard #endif /* __irpdef loaded */ #endif /* end STDIRP conditional stuff */ /* Definitions of other SWdriver structures */ #include candef #include ccbdef /* Channel control block */ #include cddbdef /* Cluster DDB def */ #include crbdef /* Controller request block */ #include cramdef /* Controller register access method */ #include dcdef /* Device codes */ #include ddbdef /* Device data block */ #include ddtdef /* Driver dispatch table */ /* temp def of nosrv bit */ #define DEV$M_NOSRV 0 /* use dev$m_fill_2 bit for serve... */ #define DEV$M_SERVESOMEHOW 134217728 /* should cover both bits */ #include devdef /* Device characteristics */ #include dptdef /* Driver prologue table */ #include dyndef /* Dynamic type definitions */ #include embdvdef /* Error log entry for devices */ #include fdtdef /* Function decision table */ #include fdt_contextdef /* Function decision table */ #include fkbdef /* Fork block */ #include idbdef /* Interrupt data block */ #include iocdef /* IOC constants */ #include iodef /* I/O function codes */ #include orbdef /* Object rights block */ #include pagedef /* Get page definitions and disk block size */ #include pcbdef /* Process control block */ #include ptedef /* Page Table entry definitions */ #include sbdef /* System block def */ #include ssdef /* System service status codes */ #include stddef /* Common definitions */ #include stsdef /* Status value fields */ #include ucbdef /* Unit control block */ #include vadef /* Virtual address definitions */ #define FDT$PS_FUNC_RTN 8 /* Define function prototypes for system routines */ #include acp_routines /* ACP$ and ACP_STD$ routines */ #include erl_routines /* erl$ and erl_std$ routines */ #include exe_routines #include ioc_routines #include sch_routines #include com_routines #include exe_routines /* exe$ and exe_std$ routines */ #include ioc_routines /* ioc$ and ioc_std$ routines */ #include ldr_routines /* ldr$ and ldr_std$ routines */ /*#include sch_routines /* sch$ and sch_std$ routines */ /* Define various device driver macros */ #include vms_drivers /* Device driver support macros */ #include vms_macros /* Define bug_check and such */ #include descrip #define IO$S_FCODE 6 /* Define the DEC C functions used by this driver */ #include builtins /* C builtin functions */ #include string /* String rtns from "kernel CRTL" */ /* Define some useful types */ typedef unsigned short int WORD; /* Define a WORD (16 bits) */ typedef unsigned char BYTE; /* Define a BYTE (8 bits) */ typedef unsigned int UINT; /* Usigned int (32 bits) */ typedef unsigned char uchar; /* Define constants specific to this driver */ enum { /* Timeout times */ TIMEOUT_TIME= 10, /* I/O Timeout time (seconds) */ DRQ_TIME = 1000*1000, /* DRQ wait time (1 millisecond) */ RESET_TIME = 2, /* Reset time (seconds) */ READY_TIME = 1000*100 /* Ready time (100 microseconds) */ }; enum { /* Controller constants */ DEVICE_IPL = 8 /* IPL of device, notice same as fork */ }; /* Define some useful constants */ /* NUM_HOSTS must be a power of 2 */ #define NUM_HOSTS 8 /* Number of paths we can handle */ enum { /* Geometry and transfer constants */ MAX_SECTOR = 256, /* Max sectors per track */ MAX_HEAD = 16, /* Max heads per cylinder */ MAX_CYLINDER= 8192, /* Max cylinders per drive */ MAX_XFER = 1, /* Max transfer size (sectors) * (127 temp to 1) */ BLK_SIZE = IOC$C_DISK_BLKSIZ, /* Size of a disk block (in bytes) */ BLK_MASK = IOC$M_BLOCK_BYTEMASK, /* "Byte within block" mask */ BLK_SHIFT = 9 /* Shift factor for blocks to bytes */ }; /* External references */ extern int MMG$GL_BWP_MASK; /* Byte-within-page mask */ extern int MMG$GL_PAGE_SIZE; /* Page size in bytes */ extern int MMG$GL_VPN_TO_VA; /* Page to byte shift count */ extern PTE *MMG$GL_SPTBASE; /* Base of system page table */ extern long SCH$GL_PCBVEC; extern long SCH$GL_MAXPIX; extern int MMG$GL_PTE_OFFSET_TO_VA;/* Shift for PTE to VA conversion */ extern DDT driver$ddt; /* Prototype DDT */ extern DPT driver$dpt; /* Prototype DPT */ extern FDT driver$fdt; /* Prototype FDT */ extern long clu$gl_allocls; /* cpu allocation class */ #ifdef DEBUG extern void ini$brk(void); /* Breakpoint routine */ #endif extern UCB *sys$ar_bootucb; /* Boot UCB */ extern int call_macro_pass_r3(int address, char* cddb); extern long exe$gl_abstim; /* Shortcuts for some of the external references */ #define _ddt driver$ddt /* Abbreviation for DDT */ #define _dpt driver$dpt /* Abbreviation for DPT */ #define _fdt driver$fdt /* Abbreviation for FDT */ /* the auxiliary structure we need if an IRP lacks enough IRP stack to hold our context data. This has been referred to at some places as the "IRP context block" though that also refers to an area of IRP stack (which must be added to irpdef). */ /* The structure we use to hold IRP context blocks */ /* If an IRP is too short for an IRP stack we use this, or if the IRP stack gets filled. */ #define IRP$K_LENGTH_V71 568 /* IRP size if no IRP stack */ /* (+ bit of slop ) */ typedef struct axs { long* axs$l_fwd; /* forward pointer */ long* axs$l_back; /* back pointer */ WORD axs$w_size; /* size */ uchar axs$b_type; /* type */ uchar axs$b_fil1; /* filler 1 */ long * axs$l_irp; /* IRP we point at */ long axs$l_orgucb; /* original UCB for IRP */ long axs$l_pid; /* irp$l_pid save */ long axs$l_orgics; /* original intercept stack state */ long axs$l_orgstat; /* IRP stat save */ long axs$l_media; /* irp$l_media save */ long axs$l_flgs; /* flags */ long axs$l_swucb; /* Switch UCB save */ #ifdef DEBUG long axs$l_func; /* irp$l_func */ #endif } AXS; /* The structure of an IRP stack entry as we use it. No headers needed. */ typedef struct axsstk { long * axs$l_irp; /* IRP we point at */ long axs$l_orgucb; /* original UCB for IRP */ long axs$l_pid; /* irp$l_pid save */ long axs$l_orgics; /* original intercept stack state */ long axs$l_orgstat; /* IRP stat save */ long axs$l_media; /* irp$l_media save */ long axs$l_flgs; /* flags */ long axs$l_swucb; /* SW UCB */ } AXSSTK; /* Define Device-Dependent Unit Control Block with extensions for SW device */ /* First, structures needed within that won't nest */ /* about all the paths. */ /* The structure in which we save info about the entries for UCBs that are intercepted. For symmetry the main UCB info is also stored here in entry 0. */ typedef struct pthptr { DT_UCB * ucb$l_hstucb; /* UCB of path */ void * ucb$l_oldmv; /* original mount ver entry */ void * ucb$l_oaltst; /* original altstart entry */ void * ucb$l_qirp; /* original pending_io entry */ void * ucb$l_cancl; /* original cancel io entry */ void * ucb$l_aux; /* original auxiliary io entry */ long ucb$l_enapth; /* path enabled flag */ uint64 ucb$q_pthtim; /* time of last I/O */ long ucb$l_pthios; /* Count of I/Os on this path */ long ucb$l_ptherr; /* Count of errors on this path */ } PTHPTR; void __PAL_IMB(void); void __MB(void); typedef struct vddt { char ddtchr[DDT$K_LENGTH]; } DDTA; /* Now the UCB extension itself */ /* Note that since we need to do address arithmetic to go from DDT to intercept UCB, define a union that will give us the offset as a sizeof operator which can then be used reasonably. */ typedef struct sw_ucb { /* we need to find UCB start, so will need to subtract a sizeof from the */ /* DDTab of the intercepted drive, so form a union here so we can do that.*/ /* union{ */ struct AhdofD { DT_UCB ucb$r_dtucb; /* Generic UCB */ union xx3{ UINT lbn; /* Block number */ struct { BYTE sec; /* Sector number */ BYTE trk; /* Track number */ WORD cyl; /* Cylinder number */ } pa; } ucb$l_media; /* end media field union */ UINT ucb$l_org_media; /* Original LBN */ void *ucb$l_org_svapte; /* Original SVAPTE address */ UINT ucb$l_org_bcnt; /* Original byte count */ UINT ucb$l_org_boff; /* Original byte offset */ union xx4 { UINT lw; /* Flags */ struct bits { UINT lba : 1; /* LBA addressing */ UINT dma : 1; /* DMA capable */ }; } ucb$l_flags; /* end flags union */ struct { /* The following area is the start of the intercept block needed for intercepts of driver entries. It has the following structure: Unique ID - generally the DPT address of the intercept driver, which will be unique for that driver on a given machine. Intercept DDT - the address of our interceptor's DDT. That is, if a second intercept occurs this cell should be filled in with a pointer to the interceptor's copy of the DDT, so this becomes a forward linked list outbound. If there is no intercept this should be null. Previous DDT - This cell holds a pointer to the DDT address as it was in the ucb$l_ddt of the intercepted driver before we intercepted the I/O and replaced the address with our own. This is an inbound list, singly linked. We use these two lists to permit intercepts to be added or removed. (Two lists are used here since the intercepted UCB in general has nowhere to keep a list head, though we need to be able to go either way.) ICsign - This cell holds a "magic number" which acts as a flag that the intercept we find is using the same scheme we are. This value is computed as hex F0070000 + DDT$K_LENGTH + 256*(length of the part of the intercept block that precedes the DDT copy). Thus it is sensitive to the amount of "stuff" preceding the DDT copy and to the DDT length. Flags 1 - Longword of flags. Only ones defined are fi8ok and nofipl8, to flag whether IPL8 postprocessing could be used with the intercept code of this intercept and all prior ones. Flags 2 - Spare longword of flags Userfiller[8] - 8 more longwords, reserved for any third parties who might need or want to keep extra information in their intercept blocks DDT - Copy of the intercepted driver's DDT, edited to point to whatever code is inserted first. The theory is that code wanting to continue with the intercepted code original function can follow the back link and use that to call the original DDT functions. Note too that the DDT contains an FDT pointer and several more, and that by inserting a copy of the original FDT with edits it is possible in like manner to chain FDT intercepts. At the end of an inserted FDT bit of code, one would locate and call the previous FDT table's routne. The presumption is also that intercepts are added by grabbing the intercepted driver's UCB$L_DDT pointer and that intercepts generally do not know of other intercepts' existence, save that when locating their intercept UCBs (or other data structures; the intercept blocks don't have to be inside UCBs) they check that the one they pick has the correct driver DPT address. Thus their intercepts would be called first, so that "inner" pointers grabbed at any stage will be valid. (That is, pointers to "previous" routine addresses grabbed by an intercept driver.) */ /* prepend start */ long ucb$l_uniqid; /* Unique ID to identify this UCB */ long * ucb$l_intcddt; /* Our interceptor's DDT addr */ long * ucb$l_prevddt; /* Previous DDT address */ long ucb$l_icsign; /* Unique "magic number" that shows */ /* that this area is a DDT intercept block in this */ /* format. The magic number encodes the DDT length */ /* to ensure compatibility in linking also. */ union xx5 { UINT lfg; /* Block number */ struct { UINT ucb$v_fi8ok : 1; /* OK to post at IPL8 */ UINT ucb$v_nofipl8 : 1; /* NOT OK to post at IPL8 */ } pa; } ucb$l_icpflgs; union xx6{ UINT lfg2; /* Block number */ struct { UINT FILLER : 1; /* filler; this for future flgs */ } pa; } ucb$l_icpflg2; /* Since this same intercept will be needed for all user intercepts also, or the traceback will fail, leave some space to be used by such intercepts should any third party applications need it. 8 longwords should be enough for anything I can currently imagine, particularly since it can be used to point to other structures should more space be needed. */ long userfiller[8]; /* Space for user intercept data */ /* prepend end */ } DI_PPD; } ucb$ahdofddt; /* char ucbpfx[10];*/ /* smaller than the struct for now */ /* }; */ /* end prefix union */ /* NOTA BENE */ /* This field must be 8 byte aligned, so be sure that the UCB ahead of it is such that this is the case. If the distance between the DDT and immediately preceding fields changes, other intercepts may not interoperate with SWDRIVER */ DDTA ucb$a_vicddt; /* Our intercept DDT copy */ /* a magic number has to go into the ucb$l_icsign long */ FDT ucb$l_myfdt; /* Copy of user FDT */ long * ucb$l_backlk; /* Points at intercepted UCB */ long * ucb$l_oldfdt; /* Pointer to previous FDT */ long * ucb$l_vict; /* Address of intercepted UCB */ long ucb$l_mungd; /* Flag that this UCB was altered */ long ucb$l_retries; /* Counter of MV packacks */ void * ucb$l_hstartio; /* Host original start_io address */ long ucb$l_mbxucb; /* UCB of mailbox to talk to server */ long ucb$l_daemon; /* IPID of server process here */ long ucb$l_sawsucc; /* Flag that we saw something not a packack*/ long ucb$l_indrct; /* Which path we are using */ long ucb$l_outstnd; /* Number of outstanding IRPs on curr pth */ long ucb$l_ptherrs; /* Counter of errors in a row */ uint64 ucb$q_swtime; /* Time of last path switch */ long * ucb$l_mypost; /* Address we'll use for post proc. */ long ucb$l_pthord[NUM_HOSTS];/* Order we look for paths in */ #ifdef DEBUG AXS * ucb$l_axsque; /* make it easy to find the axs queue */ long ucb$l_dbgsts; /* save last io status returned */ long ucb$l_irpsts; long ucb$l_irpfunc; long ucb$l_irpadr; long ucb$l_irppid; long ucb$l_irpadrp; long ucb$l_ngfunc; long ucb$l_mveirp[2]; /* irp addrs at mv end */ long ucb$l_rwt[2]; #endif IRP* ucb$l_errirp; /* IRP we replaced err code for */ IRP* ucb$l_delayirp; /* Pointer to IRP we use to delay with */ /* keep pointer around for IRP we delay with for debug */ long ucb$l_dwell; /* Dwell time for switch hysteresis */ struct sw_ucb * ucb$l_delayucb; /* Pointer to UCB we use to delay with */ long ucb$l_mscpctrl; /* CDDB controller type if an MSCP dvc */ long ucb$l_srvbits; /* mscp or qioserver serveable bits of master */ long ucb$l_errchgwedge; /* nonzero if we're changing errs to medofl */ PTHPTR ucb$a_hstdta[NUM_HOSTS]; /* Host data about each path */ PCB ucb$l_fakepcb; /* Fake PCB for MSCP cancels */ } SW_UCB; /* the "56" in the def of "magic" below must be the size of the DI_PPD area in SW_UCB in bytes, to ensure compatible intercepts. */ /* (In Macro I'd use offsets) */ #define magic (0xF0070000 + DDT$K_LENGTH + (256 * 56)) #define ucb$r_SW_ucb ucb$ahdofddt.ucb$r_dtucb.ucb$r_dpucb.ucb$r_erlucb.ucb$r_ucb #define ucb$r_SW_erl ucb$ahdofddt.ucb$r_dtucb.ucb$r_dpucb.ucb$r_erlucb #define ucb$r_SW_dp ucb$ahdofddt.ucb$r_dtucb.ucb$r_dpucb #define ucb$r_SW_dt ucb$ahdofddt.ucb$r_dtucb #define baseucb ucb->ucb$r_SW_ucb #define IS_SET(reg,bits) ( (reg & bits) == bits ) #define IS_CLEAR(reg,bits) ( (reg & bits) == 0 ) #define $SUCCESS(code) ( (code & STS$M_SUCCESS) == 1) #define $FAIL(code) ( (code & STS$M_SUCCESS) == 0) #define TRUE 1 #define FALSE 0 /* Prototypes for driver routines defined in this module */ int driver$init_tables(); void struc_init(CRB *crb, DDB *ddb, IDB *idb, ORB *orb, SW_UCB *ucb); void struc_reinit(CRB *crb, DDB *ddb, IDB *idb, ORB *orb, SW_UCB *ucb); int ctrl_init(IDB *idb, DDB *ddb, CRB *crb); int unit_init(IDB *idb, SW_UCB *ucb); void unit_init_fork(void *fr3, IDB *idb, SW_UCB *ucb); void send_daemon_msg(IRP * irp,SW_UCB* myswucb,UCB* origucb, int rtncd); int steal_cancel(int chan, IRP* irp, PCB* pcb, UCB* ucb, int reason); int steal_aux_routine(char* cddb); int steal_pending(IRP* qhdr, IRP* irp, UCB* ucb); void steal_altstart(IRP* irp, UCB* ucb); void unfixirp(IRP* irp, SW_UCB* myswucb, int npath); void fixirp(IRP* irp, SW_UCB* myswucb, int npath); void steal_mount_ver(IRP* irp, UCB* ucb); void steal_start(IRP* irp, UCB* ucb); int fdt_to_original(IRP* irp,PCB* pcb,SW_UCB* ucb,CCB* ccb); int sw_format(IRP *irp, PCB *pcb, SW_UCB *ucb, CCB *ccb); int sw_fakeformat(char* buffer, int bufsiz, SW_UCB *ucb); void mung(SW_UCB* swucb, UCB* vicucb, DDB* vicddb, SB* vicsb, long mysw); void unmung(SW_UCB* swucb, UCB* vicucb, DDB* vicddb, SB* vicsb, long mysw); void sw_findelio(IRP* irp, SW_UCB* swucb, SW_UCB* ucb); SW_UCB* getswucb(UCB* inucb); int sw_cloneducb(UCB* cloneducb, DDT* ddt, PCB* pcb, UCB* templateucb); void fixsplit(IRP* irp); void startio(IRP* irp, UCB *ucb); /* list head for aux structure */ AXS* SW_AXSHH; AXS* SW_AXSTT; /* DRIVER$INIT_TABLES - Initialize Driver Tables /* */ /* This routine is used to initialize the driver tables. The DPT, DDT */ /* and FDT structures are set up. */ /* */ /* Usage: */ /* status = driver$init_tables(); */ /* */ /* Input: */ /* none */ /* */ /* Output: */ /* none */ /* */ /* Return value: */ /* SS$_NORMAL tables successfully set up */ int driver$init_tables() { /* Finish initialization of the Driver Prologue Table (DPT) */ ini_dpt_name (&_dpt,"SWDRIVER"); /* Driver name */ ini_dpt_flags (&_dpt,DPT$M_SMPMOD); /* Set flags */ ini_dpt_maxunits (&_dpt,1000); /* 1000 units max */ ini_dpt_ucbsize (&_dpt,sizeof(SW_UCB)); /* UCB size */ ini_dpt_struc_init (&_dpt,struc_init); /* Structure init rtn */ ini_dpt_struc_reinit(&_dpt,struc_reinit); /* Structure reinit rtn */ ini_dpt_end (&_dpt); /* Finish initialization of the Driver Dispatch Table (DDT) */ ini_ddt_ctrlinit (&_ddt,ctrl_init); /* Controller init rtn */ ini_ddt_unitinit (&_ddt,unit_init); /* Unit init rtn */ ini_ddt_cloneducb (&_ddt,sw_cloneducb); /* identify cloned ucb routine */ ini_ddt_start (&_ddt,startio); /* Exec's Start I/O rtn */ ini_ddt_cancel (&_ddt,ioc_std$cancelio); /* Cancel rtn */ ini_ddt_end (&_ddt); /* Finish initialization of the Function Decision Table (FDT) */ ini_fdt_act(&_fdt,IO$_READLBLK, acp_std$readblk, DIRECT_64); ini_fdt_act(&_fdt,IO$_READPBLK, acp_std$readblk, DIRECT_64); ini_fdt_act(&_fdt,IO$_READVBLK, acp_std$readblk, DIRECT_64); ini_fdt_act(&_fdt,IO$_WRITECHECK,acp_std$readblk, DIRECT_64); ini_fdt_act(&_fdt,IO$_ACCESS, acp_std$access, BUFFERED); ini_fdt_act(&_fdt,IO$_CREATE, acp_std$access, BUFFERED); ini_fdt_act(&_fdt,IO$_DEACCESS, acp_std$deaccess, BUFFERED); ini_fdt_act(&_fdt,IO$_ACPCONTROL,acp_std$modify, BUFFERED); ini_fdt_act(&_fdt,IO$_DELETE, acp_std$modify, BUFFERED); ini_fdt_act(&_fdt,IO$_MODIFY, acp_std$modify, BUFFERED); ini_fdt_act(&_fdt,IO$_MOUNT, acp_std$mount, BUFFERED); ini_fdt_act(&_fdt,IO$_WRITELBLK, acp_std$writeblk, DIRECT_64); ini_fdt_act(&_fdt,IO$_WRITEPBLK, acp_std$writeblk, DIRECT_64) ini_fdt_act(&_fdt,IO$_WRITEVBLK, acp_std$writeblk, DIRECT_64); ini_fdt_act(&_fdt,IO$_UNLOAD, exe_std$lcldskvalid,BUFFERED_64); ini_fdt_act(&_fdt,IO$_AVAILABLE, exe_std$lcldskvalid,BUFFERED_64); ini_fdt_act(&_fdt,IO$_PACKACK, exe_std$lcldskvalid,BUFFERED_64); ini_fdt_act(&_fdt,IO$_NOP, exe_std$zeroparm, BUFFERED_64); ini_fdt_act(&_fdt,IO$_PHYSICAL, sw_format, DIRECT); ini_fdt_act(&_fdt,IO$_SEEK, exe_std$oneparm, BUFFERED); ini_fdt_act(&_fdt,IO$_FORMAT, exe_std$oneparm, BUFFERED); ini_fdt_act(&_fdt,IO$_SETMODE, exe_std$setchar, BUFFERED_64); ini_fdt_act(&_fdt,IO$_SETCHAR, exe_std$setchar, BUFFERED_64); ini_fdt_act(&_fdt,IO$_SENSEMODE, exe_std$sensemode, BUFFERED_64); ini_fdt_act(&_fdt,IO$_SENSECHAR, exe_std$sensemode, BUFFERED_64); ini_fdt_end(&_fdt); /* If we got this far then everything worked, so return success. */ return SS$_NORMAL; /* Return with success status */ } /* STRUC_INIT - Device Data Structure Initialization Routine /* */ /* This routine is used to initialize the data structures at driver */ /* loading time. */ /* */ /* Usage: */ /* struc_init(crb, ddb, idb, orb, ucb) */ /* */ /* Input: */ /* crb pointer to CRB */ /* ddb pointer to DDB */ /* idb pointer to IDB */ /* orb pointer to ORB */ /* ucb pointer to UCB */ /* */ /* Output: */ /* none */ /* */ /* Return value: */ /* none */ void struc_init(CRB *crb, DDB *ddb, IDB *idb, ORB *orb, SW_UCB *ucb) { /* Initialize the fork lock and device IPL fields */ baseucb.ucb$b_flck = SPL$C_IOLOCK8; /* set up fork lock index */ baseucb.ucb$b_dipl = DEVICE_IPL; /* and device IPL (8 for this case!)*/ /* Initialize some UCB fields */ /* we do NOT set the DEV$M_FOD bit since we don't want file operations on the switch driver. No need for such. */ baseucb.ucb$l_devchar = (DEV$M_DIR + DEV$M_AVL + DEV$M_ELG + DEV$M_IDV + DEV$M_ODV + DEV$M_SHR + DEV$M_RND); baseucb.ucb$l_devchar2 = DEV$M_NLT; /* Not a "last trk" dvc */ baseucb.ucb$b_devclass = DC$_MISC; /* Device class is random dvc */ baseucb.ucb$b_devtype = DT$_FD2; /* Device type for DDR */ baseucb.ucb$w_devbufsiz= BLK_SIZE; /* Size of a block */ baseucb.ucb$l_devsts = UCB$M_NOCNVRT; /* Do NOT convert LBNs */ return; } /* STRUC_REINIT - Device Data Structure Re-Initialization Routine /* */ /* This routine is used to reinitialize the data structures at driver */ /* reloading time. */ /* */ /* Usage: */ /* struc_init(crb, ddb, idb, orb, ucb) */ /* */ /* Input: */ /* crb pointer to CRB */ /* ddb pointer to DDB */ /* idb pointer to IDB */ /* orb pointer to ORB */ /* ucb pointer to UCB */ /* */ /* Output: */ /* none */ /* */ /* Return value: */ /* none */ void struc_reinit (CRB *crb, DDB *ddb, IDB *idb, ORB *orb, SW_UCB *ucb) { ddb->ddb$ps_ddt = &_ddt; /* Point ddb to the ddt */ return; /* Return to caller */ } /* sw-format fdt - IO$_PHYSICAL local FDT processing */ /* */ /* This routine handles the FDT processing for the IO$_PHYSICAL */ /* function. This function is used by SWdriver to do initialization and */ /* setup so that its server need not know the format of its local UCB */ /* extension. It takes one argument, a buffer whose contents are */ /* interpreted to perform the operations. Connecting or disconnecting */ /* to a disk are included in functions here. */ /* */ /* Input: */ /* irp pointer to IRP */ /* pcb pointer to PCB */ /* ucb pointer to UCB */ /* ccb pointer to CCB */ /* P1,P2 point to an input control block. */ /* Buffer formats: ufunc uswitch lo uswitch hi ulen rest 1 dvc name len dvc name bash 2 dvc name len dvc name unbash 3 unit to sw to idlfgs=0 switch, err if dvc bsy 3 " idlfgs=1 save next irp, busy dvc 3 " idlfgs=2 restart i/o, unbusying dvc, no switch 4 unit to rpt ? statblk len statblk data rpt stats Returns statistics on device paths this SW unit is using in an array. 5 UCB of device x x returns SW UCB, pathpointer array address, and some other info. Switch part of array gets the SW UCB or zero if none is found. This request can be sent to ANY SWdriver unit for any device to find out if the device is intercepted. It will return the SW ucb and info about the SWdriver unit that intercepted the device if it is intercepted. 6 IPID of server (both halves) mbx name len mbx name Sets the mailbox UCB and server IPID (if they are legal & found) into this SW driver unit UCB so it will communicate with the mailbox to talk to the server. 7 x x x x x Clears the SWdriver's references to the server. 8 Returns a magic number for this driver, adding 4 if the driver is online. Use from user mode code to ensure you are talking to swdriver. 9 As 1 and 2, but ulen is device UCB, not name. DDB and SB are 10 obtained from the UCB pointer and DDB pointer. This is done to simplify kernel mode setup. 11 x x x x x Returns SW UCB pointer, value of ucb$l_indrct, and the PTHPTR structure addresses of all PTHPTR structures, and of the one which is current. 12 Functions like 5, except that no input UCB need be supplied. It returns information about the devices the pointed-to SW device has associated with it. */ /* */ /* Output: */ /* */ /* Return value: */ /* SS$_FDT_COMPL shows that the routine completed correctly */ int sw_format(IRP *irp, PCB *pcb, SW_UCB *ucb, CCB *ccb) { /* Define the input buffer type as a union struct so each function can find a convenient type to use. */ #define MPSZ 128 #define MPSZQ 64 #define MPDN 512 /* Number of paths that will fit for statistics report */ #define MPNPTH ((MPSZ-5)/4) typedef struct inbuf { long ufunc; /* user function code (eg, bash/unbash) */ long uswitch; /* info passed in (eg add, which unit, encoded) */ long ulen; /* length of device name passed */ union { char dvcname[MPDN]; long moreprm[MPSZ]; } ; } INBUF; typedef struct qinbuf { long ufunc; /* user function code (eg, bash/unbash) */ long uswitch; /* info passed in (eg add, which unit, encoded) */ long ulen; /* length of device name passed */ union { char dvcname[MPDN]; uint64 moreprm[MPSZQ]; } ; } QINBUF; INBUF InBuf; int tstat; int tfcn; SB* mysb; INBUF * ibp; QINBUF * qibp; UCB* myucb; DDB* myddb; UCB* scratchucb; PTHPTR* mypthptr; SW_UCB* myswucb; /* allow us to pull a quad into 2 longs */ union { uint64 ttim; long ttiml[2]; }timu; long myswitch; int trtns; int myswmod; int myunit; SW_UCB* scrswucb; int ufcode; int savipl; int curunit; /* currently used subunit, for where we are switching */ int kk,kkk,kkkk; /* temps */ int i,ii,iii; /* temps */ PCB* trypcb; UCB* tstucb; DP_UCB* mydpucb; long* pcbptr; PTHPTR * newpath; struct dsc$descriptor_s mydvcnm; /* Ensure that the function is called with some modifier as a protection against someone randomly trying to format the SW device */ if (irp->irp$v_fmod == 0) { return (fdt_to_original(irp,pcb,ucb,ccb)); } /* check the buffer is writeable */ /* Also ensure it is the size we expect. */ tstat = exe_std$writechk(irp,pcb,(UCB*)ucb,(void*)irp->irp$q_qio_p1, irp->irp$q_qio_p2); if (!$VMS_STATUS_SUCCESS(tstat) || irp->irp$q_qio_p2 < sizeof(INBUF)){ return (exe_std$abortio(irp,pcb,(UCB*)ucb,SS$_BADPARAM)); }; myswucb = (SW_UCB*) ucb; tfcn = irp->irp$v_fcode; ibp = (INBUF*) irp->irp$q_qio_p1; qibp = (QINBUF*)ibp; scrswucb = ucb; /* Set up the descriptor */ mydvcnm.dsc$w_length = strlen(ibp->dvcname); mydvcnm.dsc$b_class = DSC$K_CLASS_S; mydvcnm.dsc$a_pointer = &ibp->dvcname[0]; mydvcnm.dsc$b_dtype = DSC$K_DTYPE_T; myswitch = ibp->uswitch; /* extract word fields out of "myswitch" */ myunit = myswitch & 65535; myswmod = (myswitch >> 16); ufcode = ibp->ufunc; scratchucb = (UCB*) ucb; switch (ufcode) { /* 1 bashes disk, 2 unbashes */ /* 3 returns swdriver address and pthptr address if bashed by it. */ /* In these cases the "myswitch" arg low word is the unit to set or clear */ case 1:{ trtns = 1; mydvcnm.dsc$a_pointer = & ibp->dvcname[0]; mydvcnm.dsc$w_length = ibp->ulen; #ifdef DEBUG /* ini$brk(); */ #endif sch_std$iolockw(pcb); tstat = ioc_std$searchdev(&mydvcnm,&myucb,&myddb,&mysb); if ($VMS_STATUS_SUCCESS(tstat)){ /* in this case, "myswitch" is the index (counting from zero) of the path within this tuple for the switch driver. */ if (myswmod > 0){ /* if switch modifier exists, use "2p" alias here. */ mydpucb = (DP_UCB*)myucb; if ((myucb->ucb$l_devchar2 & DEV$M_2P) != 0){ /* If we are supposed to use the alternate UCBs, set that linkage up */ myddb = (DDB*)mydpucb->ucb$l_2p_ddb; myucb = mydpucb->ucb$l_2p_altucb; } } if(myucb->ucb$v_online){ mung(scrswucb,myucb,myddb,mysb,myswitch); } else { trtns = 32; } } else { /* can't find device. Err...*/ /* Must place error in the fdt context area */ } sch_std$iounlock(pcb); irp->irp$l_media = trtns; /* return good status */ return(exe_std$finishio(irp,(UCB*)ucb)); break; } case 2:{ mydvcnm.dsc$a_pointer = & ibp->dvcname[0]; mydvcnm.dsc$w_length = ibp->ulen; sch_std$iolockw(pcb); tstat = ioc_std$searchdev(&mydvcnm,&myucb,&myddb,&mysb); if ($VMS_STATUS_SUCCESS(tstat)){ if (myswmod > 0){ /* if switch modifier exists, use "2p" alias here. */ mydpucb = (DP_UCB*)myucb; if ((myucb->ucb$l_devchar2 & DEV$M_2P) != 0){ /* If we are supposed to use the alternate UCBs, set that linkage up */ myddb = (DDB*)mydpucb->ucb$l_2p_ddb; myucb = mydpucb->ucb$l_2p_altucb; } } unmung(scrswucb,myucb,myddb,mysb,myswitch); }; sch_std$iounlock(pcb); irp->irp$l_media = 1; /* return good status */ return(exe_std$finishio(irp,(UCB*)ucb)); break; } /* more options here */ /* Rather than use a different code, we will use this area to do manual switching also. The buffer format will in this case use the myswitch variable as two words also. The format will be Word 0: unit to switch to (0 to NUM_HOSTS) Word 1: idle-device flags: If 0, just return an error if the device was busy (active I/O count non zero) and return success only if the unit was idle and successfully switched. If 1, flag startio intercept to save the next IRP address and leave the device busy until we later unbusy it. The unbusy call will be able to switch or not. If 2, restart I/O with no switch using the saved IRP if any. If there is none, just return. Note that the busying will not alter the outstanding I/O count ucb$l_outstnd since it is purely inside the switching driver here. The caller can block or unblock I/O but will need to wait for I/O to drain to switch paths; this is easier in user mode so we'll leave it for that. We'll restart I/O by forking and at fork level running REQCOM. */ case 3:{ /* in this case, "myswitch" is the index (counting from zero) of the path within this tuple for the switch driver. */ /* Again unit 65535 means "the next enabled unit" */ if (myunit > NUM_HOSTS){ for (kkk = 1; kkk <= NUM_HOSTS; kkk++){ /* get index of the next path (but go far enough to come back if we must) */ kkkk = (kkk + ucb->ucb$l_indrct)%NUM_HOSTS; newpath = &ucb->ucb$a_hstdta[kkkk]; if (newpath->ucb$l_enapth){ myunit = kkkk; break; } } } if (myunit > NUM_HOSTS){ /* if still above maxunits, error return */ return(exe_std$abortio(irp,pcb,(UCB*)ucb,SS$_ABORT)); } newpath = &ucb->ucb$a_hstdta[myunit]; switch (myswmod){ case 0:{ if ((ucb->ucb$l_outstnd != 0 && irp->irp$l_qio_p4 != 31416)){ return(exe_std$abortio(irp,pcb,(UCB*)ucb,SS$_IVSTSFLG)); } else { /* OK, looks like we should switch the unit. Do so. */ /* (MANUAL switch here !! ) */ device_lock(scratchucb->ucb$l_dlck,1,&savipl); /* *** fix this *** */ ucb->ucb$l_indrct = myunit; ucb->ucb$l_dwell = exe$gl_abstim; ucb->ucb$l_retries = 0; device_unlock((struct _spl*)scratchucb->ucb$l_dlck,savipl,1);/* *** fix this *** */ return(exe_std$finishio(irp,(UCB*)ucb)); } } case 1:{ /* flag this IRP is really for us and send to normal processing for the host device. If no host device just junk it. */ /* Since this is just a $qio, just queue it to the prime channel via exe$std_insioqc after setting irp$q_qio_p5 to the swucb value we have */ /* The start code to interpret this must exist ... doesn't yet */ if (ucb->ucb$l_delayirp != 0) return(exe_std$abortio(irp,pcb,(UCB*)ucb,SS$_IVSTSFLG)); newpath = &ucb->ucb$a_hstdta[0]; (long)irp->irp$q_qio_p5 = (long)ucb; ucb->ucb$l_errchgwedge = 0; ucb->ucb$l_delayirp = irp; irp->irp$l_ucb = (UCB*)newpath->ucb$l_hstucb; ucb->ucb$l_delayucb = ucb; exe_std$insioqc(irp, (UCB*)newpath->ucb$l_hstucb); /* do NOT finish the I/O up yet. */ /* The IRP we queued to the target will not finish just yet but we get thru the FDT stuff here. The IRP will finish due to stuff in startio and our later call that will finish the IRP */ return SS$_FDT_COMPL; /* exit */ } case 2:{ /* Here we finish up the IRP that was queued before. */ /* if QIO P4 is a -1, force outstanding I/O to zero. */ if(irp->irp$l_qio_p4 == 31416)ucb->ucb$l_outstnd = 0; if (ucb->ucb$l_delayirp == 0) return(exe_std$abortio(irp,pcb,(UCB*)ucb,SS$_IVSTSFLG)); /* Now we know that startio got the IRP. Whether or not there has been a call to switch units, complete that I/O. */ /* gotta fork here and finish the IRP in fork level */ fork(sw_findelio,(__int64)ucb->ucb$l_delayirp,(__int64)ucb->ucb$l_delayucb,(UCB*)ucb); exe_std$finishio(irp,(UCB*)ucb); return SS$_FDT_COMPL; } default:{ return(exe_std$abortio(irp,pcb,(UCB*)ucb,SS$_BADPARAM)); } } sch_std$iounlock(pcb); irp->irp$l_media = 1; /* return good status */ return(exe_std$finishio(irp,(UCB*)ucb)); break; } case 4:{ /* Use subfunction 4 for reporting statistics, which include I/O counts and error counts for all subpaths, and things like time of last switch and anything else that looks worth having. We'll just dump the numbers into the user's buffer which we know to be writable already. */ ibp->moreprm[0] = NUM_HOSTS; /* Tell how long tables are */ ibp->moreprm[1] = sizeof(PTHPTR); /* Tell how long path tbl entry is*/ ibp->moreprm[2] = myswucb->ucb$l_indrct; /* return which is current path */ qibp->moreprm[2] = myswucb->ucb$q_swtime; kkk = 6; i = 0; /* use to count paths we actually see */ for (kk = 0; kk < NUM_HOSTS; kk++){ mypthptr = &ucb->ucb$a_hstdta[kk]; if (kk < MPNPTH){ /* Store statistics data for output. */ /* The data is I/O count, error count, and time of last I/O for each path */ if ((long)mypthptr->ucb$l_hstucb < 0) i++; ibp->moreprm[kkk++] = mypthptr->ucb$l_pthios; /* I/O count this path */ ibp->moreprm[kkk++] = mypthptr->ucb$l_ptherr; /* error cnt */ timu.ttim = mypthptr->ucb$q_pthtim; /* get time */ ibp->moreprm[kkk++] = timu.ttiml[0]; /* i/o time */ ibp->moreprm[kkk++] = timu.ttiml[1]; /* i/o time */ } } ibp->moreprm[0] = i; irp->irp$l_media = 1; /* return good status */ return(exe_std$finishio(irp,(UCB*)ucb)); break; } case 5:{ /* Use subfunction 5 to get back information about bashes. In this case we expect we are being called by some external code which needs to find out if the device being passed is in fact having its path intercepted by swdriver. Rather than make other code need to know the details of our internal structures, use this code to export the knowledge. On input look for a device UCB address, and if it is intercepted by a SW UCB return the SW unit number and the pthptr array address. */ if (myswitch < 0){ /* UCB address looks potentially valid */ myswucb = getswucb((UCB*) myswitch); ibp->ulen = (long)myswucb; /* Return the SW ucb unit we found or zero */ scratchucb=(UCB*)myswucb; if ((long)scratchucb < 0){ ibp->moreprm[0] = scratchucb->ucb$w_unit; /* return SW unit no. */ ibp->moreprm[1] = &myswucb->ucb$a_hstdta[0]; /* Rtn addr of path pointer array also. */ ibp->moreprm[2] = myswucb->ucb$l_indrct; /* return which is current path */ ibp->moreprm[3] = &myswucb->ucb$l_pthord[0]; /* path sel order addr */ ibp->moreprm[4] = NUM_HOSTS; /* Tell how long tables are */ ibp->moreprm[5] = sizeof(PTHPTR); /* Tell how long path tbl entry is*/ ibp->moreprm[6] = (long)myswucb->ucb$l_vict; /* intercepted ucb */ ibp->moreprm[7] = myswucb->ucb$l_outstnd; /* Current I/O count */ } } else { /* error to fdt */ } /* complete this I/O */ irp->irp$l_media = 1; /* return good status */ return(exe_std$finishio(irp,(UCB*)ucb)); break; } case 6:{ /* Use subfunction 6 to insert the server mailbox UCB and PID */ /* Input in device name area is MB device name stuff so we look it up (to reduce kernel code elsewhere) and PID in the other long. */ mydvcnm.dsc$a_pointer = & ibp->dvcname[0]; mydvcnm.dsc$w_length = ibp->ulen; sch_std$iolockw(pcb); tstat = ioc_std$searchdev(&mydvcnm,&myucb,&myddb,&mysb); if ($VMS_STATUS_SUCCESS(tstat) && (myswitch > 0)){ /* in this case, "myswitch" is the index (counting from zero) of the path within this tuple for the switch driver. */ ibp->uswitch = myucb; /* return mbx ucb */ ucb->ucb$l_mbxucb = myucb; /* Let's be sure the IPID here is a process IPID that really exists*/ pcbptr = (long*) SCH$GL_PCBVEC; if ((long)pcbptr >= 0)return; kkk = 0; for (kk=0; kk < SCH$GL_MAXPIX; kk++){ trypcb = *(PCB**)pcbptr++; if (trypcb->pcb$l_pid == myswucb->ucb$l_daemon ){ kkk = 1; break; } } if (kkk == 1){ ucb->ucb$l_daemon = myswitch; /* IPID of the daemon */ } irp->irp$l_media = 1; /* return good status */ } else { /* can't find device. Err...*/ /* Must place error in the fdt context area */ } sch_std$iounlock(pcb); return(exe_std$finishio(irp,(UCB*)ucb)); /* complete this I/O */ break; } case 7:{ /* Function 7 just deletes the reference to the server */ ucb->ucb$l_daemon = 0; ucb->ucb$l_mbxucb = 0; irp->irp$l_media = 1; /* return good status */ return(exe_std$finishio(irp,(UCB*)ucb)); break; } case 8:{ /* Function 8 sanity checks that this IS an SW driver by returning a magic number, octal 14747 or 6631 decimal. */ /* This is the pdp11 opcode for "mov -(pc),-(pc)" which instruction copies itself backwards in memory until it wraps around 32 kw. It is weird but odd, so will be taken for a kind of success. */ irp->irp$l_iost2 = ucb->ucb$l_indrct; irp->irp$l_media = 6631; /* Add 4 if the SW unit is offline */ /* This should generally be the case for first-time access */ if (ucb->ucb$r_SW_ucb.ucb$v_valid == 0) irp->irp$l_media = irp->irp$l_media + 4; return(exe_std$finishio(irp,(UCB*)ucb)); break; } case 9:{ #ifdef DEBUG /* ini$brk();*/ #endif trtns = 8; sch_std$iolockw(pcb); /* tstat = ioc_std$searchdev(&mydvcnm,&myucb,&myddb,&mysb);*/ if ((ibp->ulen) < 0){ myucb = (UCB*)ibp->ulen; myddb = myucb->ucb$l_ddb; mysb = (struct _sb *)myddb->ddb$l_sb; /* in this case, "myswitch" is the index (counting from zero) of the path within this tuple for the switch driver. */ if (myswmod > 0){ /* if switch modifier exists, use "2p" alias here. */ mydpucb = (DP_UCB*)myucb; if ((myucb->ucb$l_devchar2 & DEV$M_2P) != 0){ /* If we are supposed to use the alternate UCBs, set that linkage up */ myddb = (DDB*)mydpucb->ucb$l_2p_ddb; myucb = mydpucb->ucb$l_2p_altucb; } } if(myucb->ucb$v_online){ mung(scrswucb,myucb,myddb,mysb,myswitch); trtns = 1; } else { trtns = 32; /* rtn bad status if we fail */ } irp->irp$l_media = trtns; /* return good status */ } else { /* can't find device. Err...*/ /* However we manage by trtns mech, to return a failure */ irp->irp$l_media = trtns; /* return good status */ } sch_std$iounlock(pcb); return(exe_std$finishio(irp,(UCB*)ucb)); break; } case 10:{ sch_std$iolockw(pcb); /* tstat = ioc_std$searchdev(&mydvcnm,&myucb,&myddb,&mysb);*/ if ((ibp->ulen) < 0){ if (myswmod > 0){ myucb = (UCB*)ibp->ulen; myddb = myucb->ucb$l_ddb; mysb = (struct _sb *)myddb->ddb$l_sb; /* if switch modifier exists, use "2p" alias here. */ mydpucb = (DP_UCB*)myucb; if ((myucb->ucb$l_devchar2 & DEV$M_2P) != 0){ /* If we are supposed to use the alternate UCBs, set that linkage up */ myddb = (DDB*)mydpucb->ucb$l_2p_ddb; myucb = mydpucb->ucb$l_2p_altucb; } } unmung(scrswucb,myucb,myddb,mysb,myswitch); irp->irp$l_media = 1; /* return good status */ }; sch_std$iounlock(pcb); return(exe_std$finishio(irp,(UCB*)ucb)); break; } case 11:{ sch_std$iolockw(pcb); /* Return the path block for this SW unit */ if ((ibp->ulen) > 0){ /* fill in outputs here */ ibp->ulen = (long)myswucb; /* Return the SW ucb unit we found or zero */ scratchucb=(UCB*)myswucb; if ((long)scratchucb < 0){ ibp->moreprm[0] = myswucb->ucb$l_indrct; /* return path in use */ ibp->moreprm[1] = &myswucb->ucb$a_hstdta[0]; /* Rtn addr of path pointer */ ibp->moreprm[2] = &myswucb->ucb$a_hstdta[myswucb->ucb$l_indrct]; /* Rtn addr of path pointer */ irp->irp$l_media = 1; /* return good status */ } }; sch_std$iounlock(pcb); return(exe_std$finishio(irp,(UCB*)ucb)); break; } case 12:{ /* Use subfunction 12 to get back information about bashes. In this case we report on the SW device that the function invokes. */ irp->irp$l_media = 1; /* return good status default*/ if ((long)ucb < 0){ /* UCB address looks potentially valid */ myswucb = ucb; ibp->ulen = (long)myswucb; /* Return the SW ucb unit we found or zero */ scratchucb=(UCB*)myswucb; if ((long)scratchucb < 0){ ibp->moreprm[0] = scratchucb->ucb$w_unit; /* return SW unit no. */ ibp->moreprm[1] = &myswucb->ucb$a_hstdta[0]; /* Rtn addr of path pointer array also. */ ibp->moreprm[2] = myswucb->ucb$l_indrct; /* return which is current path */ ibp->moreprm[3] = &myswucb->ucb$l_pthord[0]; /* path sel order addr */ ibp->moreprm[4] = NUM_HOSTS; /* Tell how long tables are */ ibp->moreprm[5] = sizeof(PTHPTR); /* Tell how long path tbl entry is*/ ibp->moreprm[6] = (long)myswucb->ucb$l_vict; /* intercepted ucb */ ibp->moreprm[7] = myswucb->ucb$l_outstnd; /* Current I/O count */ irp->irp$l_media = 1; /* return good status */ } } else { /* error */ irp->irp$l_media = SS$_BADPARAM; /* return fail status */ } /* complete this I/O */ return(exe_std$finishio(irp,(UCB*)ucb)); break; } /* more options here */ default: {return (exe_std$abortio(irp,pcb,(UCB*)ucb,SS$_BADPARAM)); break;} } return (exe_std$abortio(irp,pcb,(UCB*)ucb,SS$_BADPARAM)); /* return SS$_FDT_COMPL; */ /* and exit */ } /* finish delayed io routine, a fork routine */ /* Abstract: The sw_findelio routine finishes up I/O on the IRP that was used to idle a device, permitting the target device I/O to resume. Inputs: irp The IRP that was used for delay swucb The UCB of the SW device ucb Target device UCB Outputs: I/O on the IRP is completed and target I/O resumes. */ void sw_findelio(IRP* irp, SW_UCB* swucb, SW_UCB* ucb){ /* just finish the I/O and flag we did so. */ swucb->ucb$l_delayirp = 0; /* flag we completed this */ /* if (ucb->ucb$l_outstnd == 0){*/ ucb->ucb$r_SW_ucb.ucb$l_irp = irp; ioc_std$reqcom(SS$_NORMAL,0,(UCB*)ucb); /* }*/ } /* mung subfunction. */ /* Abstract: */ /* This function does the actual stealing of the DDT from a device and */ /* sets up the intercept chain properly for the new DDT intercept. */ /* Expects to hold i/o mutex. */ /* */ /* Inputs: swucb - UCB of SW device which will control use of the path vicucb - UCB of device being added as a path controlled vicddb - DDB of device being added as a controlled path vicsb - SB of device being added " " " " mysw - index being added into the tuple (0-max). Greater than the max paths implies use next free path. Outputs: vicucb is intercepted if this is the first path being added to the SW device. Otherwise, entry points for the path are recorded, as is its UCB address. Vicucb UCB is hidden from system access. If the intercept is not the first, it will add to existing ones provided the format standards here are followed. If they are not, intercept will not take place. Synchronization: Takes out fork lock and returns it. Requires IO write mutex be held on call. */ void mung(SW_UCB* swucb, UCB* vicucb, DDB* vicddb, SB* vicsb, long mysw){ int myunit; int savipl; DDT* vicddt; DDT* swddt; FDT* vicfdt; FDT* swfdt; DDT* swcpyddt; SW_UCB* svicucb; long* long1; long * long2; long * long3; /* scratch pointers */ long * long4; PTHPTR * thisbash; PTHPTR * wrkbash; int thisunit; int kk,kkk,kkkk; UCB* scratchucb; SW_UCB* icucb; (SW_UCB*)scratchucb = swucb; myunit = mysw; thisunit = mysw & 65535; if (thisunit != 0 && swucb->ucb$l_mungd == 0) return; fork_lock(scratchucb->ucb$b_flck,(int*)&savipl); vicddt = vicucb->ucb$l_ddt; /* On entry we need to examine the "mysw" argument. It has the following structure: 1. word, the device we are adding to the tuple. If not zero, then unit zero MUST have been filled in earlier. */ /* check that if the device being added to the tuple is nonzero, that unit zero exists and is set up already. If this passes, be sure also that the unit being added in is not already in use for something. As a convenience we will "rule" that a "unit" of 65535 means the next free unit. The user interface can use this, but preserve the ability lower down to specify. */ if (thisunit != 0 && swucb->ucb$l_mungd == 0) return; svicucb = (SW_UCB*)vicucb; if (thisunit > NUM_HOSTS){ for (kk=0; kk < NUM_HOSTS; kk++){ wrkbash=&swucb->ucb$a_hstdta[kk]; if (wrkbash->ucb$l_hstucb == 0){ thisunit = kk; break; } } } if (thisunit >= NUM_HOSTS) return; /* Now we know that "thisunit" is in the permitted range. Since we ANDed with 65535 before, it can't very well be negative. */ thisbash = &swucb->ucb$a_hstdta[0]; if (thisunit != 0 ){ if (thisbash->ucb$l_hstucb == 0) return; /* Return if this is a "second" (or later) path and no first is set up. */ } thisbash = &swucb->ucb$a_hstdta[thisunit]; if (thisunit != 0 ){ if (thisbash->ucb$l_hstucb != 0) return; /* Return if this is a "second" (or later) path and previously set up. */ } /* */ /* If we are doing unit 0 here we replace DDT and also make an edited FDT copy. If doing other units, we just fill in the PTHPTR structure in the intercept UCB. */ if (thisunit == 0){ /* See if this is the original DDT */ if ((long)vicddt != (long)vicddb->ddb$l_ddt) { /* Not the same, so the UCB looks to have been intercepted */ /* Check that the intercept followed our standard */ /* First make a pointer to the start of the SW UCB presuming the intercept IS using our methods. If it is not, as a general matter we are going to reference a few longs ahead of the DDTAB, and give up unless our magic number is there. This will always be OK if we are using the same intercept as the other guy, almost always will be fine other times, and for switching will basically always be OK because the switch intercept will be put in place at load time and is almost guaranteed to be first. This stuff is in here, though, in case of later users of the technique and (importantly) for test uses where someone may manually command to connect a few UCBs into a test tuple. */ (char*) icucb = (char*)vicddb->ddb$l_ddt - (long)offsetof(SW_UCB,ucb$a_vicddt); if (icucb->ucb$ahdofddt.DI_PPD.ucb$l_icsign != magic){ fork_unlock(scratchucb->ucb$b_flck,savipl,SMP_RESTORE); return; } /* The magic pattern looks OK, so we should be able to store the new DDT address in OUR intercept UCB into the victim's forward pointer and the back address to his in our interecept UCB. */ (long)icucb->ucb$ahdofddt.DI_PPD.ucb$l_intcddt = (long)&svicucb->ucb$a_vicddt; swucb->ucb$ahdofddt.DI_PPD.ucb$l_icpflgs.pa.ucb$v_fi8ok = icucb->ucb$ahdofddt.DI_PPD.ucb$l_icpflgs.pa.ucb$v_fi8ok; (long)swucb->ucb$ahdofddt.DI_PPD.ucb$l_prevddt = (long)vicucb->ucb$l_ddt; /* end for now of second bash stuff */ } else { /* New bash of a never intercepted UCB */ swucb->ucb$l_mungd = 1; swucb->ucb$ahdofddt.DI_PPD.ucb$l_prevddt = (long*)vicucb->ucb$l_ddt; swucb->ucb$ahdofddt.DI_PPD.ucb$l_intcddt = 0; swucb->ucb$l_vict = vicucb; /* test here would let us know not to set finipl8 fit */ /* Otherwise it's a new intercept; set finipl8 ok flag */ if (svicucb->ucb$ahdofddt.DI_PPD.ucb$l_icpflgs.pa.ucb$v_nofipl8 == 0)swucb->ucb$ahdofddt.DI_PPD.ucb$l_icpflgs.pa.ucb$v_fi8ok = 1; /* copy the victim ddt to our site */ memcpy(&swucb->ucb$a_vicddt,vicucb->ucb$l_ddt,sizeof(DDT)); swcpyddt = (DDT*)&swucb->ucb$a_vicddt; /* copy FDT stuff too */ /* Some implicit information is used here about the format of a driver FDT table. */ /* Specifically, FDT$PS_FUNC_RTN must be 8 (8 = 2* sizeof(long) )*/ swucb->ucb$l_oldfdt =(long*)swcpyddt->ddt$ps_fdt_2; memcpy(&swucb->ucb$l_myfdt,vicddt->ddt$ps_fdt_2,sizeof(FDT)); /* now edit this copy of his FDT to use SW routines if present */ /* note: involves some knowledge of step 2 fdt tbl format. */ (long)long1 = (long)&swucb->ucb$l_myfdt + ( 2 * sizeof(long)); long2 = (long*) &driver$fdt; (long)long2 = (long) long2 + ( 2 * sizeof(long)); /* now we're pointing at the 64 function pointers */ long3 = (long*)&EXE$ILLIOFUNC; /* What illegal funcs get filled in with */ long4 = (long*)&sw_format; /* Our IO$_PHYSICAL routine */ for (kkk=0; kkk < 64; kkk++){ /* If the SW FDT does NOT point to illiofunc and does not point to our IO$_PHYSICAL then we want the replacement FDT to point at our routines (first; they will call the original ones afterwards). */ if (((long)*long2 != (long)long3) && ((long)*long2 != (long)long4)){ *long1 = (long)long2; /* Make the new FDT routine point at our stuff*/ } /* Bump our pointers past this entry and look at the next now */ long1++; long2++; } /* At this point the replacement FDT routine contains our FDT pointers */ /* Also, the replacement IO$_PHYSICAL is unchanged; to control the SW device */ /* an app should send that function to the SW device, not to some intercepted */ /* one. By ensuring that the FDT routines change only where the SW device has */ /* some other interception, we facilitate our later use of this code. It may */ /* be desired in the future to have FDT routines switched, and with this code */ /* in place, that switching is simple. */ } /* Now save other pointers we need before switching anything */ /* First, for convenience of debug, store a couple pointers in the intercept block filler area, one to the SW UCB, one to the start of the host data arrays. */ swucb->ucb$ahdofddt.DI_PPD.userfiller[0] = (long)swucb; swucb->ucb$ahdofddt.DI_PPD.userfiller[1] = (long)&swucb->ucb$a_hstdta[0]; /* original start-io */ (void *)swucb->ucb$l_hstartio = (void *)vicddt->ddt$ps_start_2; /* Main UCB address, both copies */ swucb->ucb$l_backlk = (long*)vicucb; /* Point the DDT copy's start_2 address at our routine, and similarly with */ /* all other entries we need to intercept, after saving the originals */ /* (we save the originals for speed of access) */ thisbash->ucb$l_hstucb = (struct _dt_ucb*)vicucb; thisbash->ucb$l_oldmv = (long*)vicddt->ddt$ps_mntver_2; thisbash->ucb$l_oaltst = (long*)vicddt->ddt$ps_altstart_2; thisbash->ucb$l_qirp = (long*)vicddt->ddt$ps_pending_io; /* N.B. - altstart routine is called via JSB so we need some special linkage and a macro jacket to call out from there. */ thisbash->ucb$l_aux = (long*)vicddt->ddt$l_aux_routine; thisbash->ucb$l_cancl = (long*)vicddt->ddt$ps_cancel_2; thisbash->ucb$l_enapth = 1; thisbash->ucb$q_pthtim = 0; thisbash->ucb$l_pthios = 0; thisbash->ucb$l_ptherr = 0; /* initialize all other entries */ /* Now insert the internal entries into the DDT copy we have made. Note that this copy is NOT yet active at this moment. */ (void*)swcpyddt->ddt$ps_mntver_2 = (void*)&steal_mount_ver; (void*)swcpyddt->ddt$ps_altstart_2 = (void*)&steal_altstart; (void*)swcpyddt->ddt$ps_pending_io = (void*)&steal_pending; (void*)swcpyddt->ddt$ps_start_2 = (void*)&steal_start; (void*)swcpyddt->ddt$l_aux_routine = (void*)&steal_aux_routine; (void*)swcpyddt->ddt$ps_cancel_2 = (void*)&steal_cancel; /* Now replace the DDT pointer in the victim's UCB, bracketing with IMB */ __PAL_IMB(); vicucb->ucb$l_ddt = swcpyddt; __PAL_IMB(); } else { /* thisunit == 0 */ thisbash->ucb$l_hstucb = (struct _dt_ucb *)vicucb; (void*)thisbash->ucb$l_oldmv = (void*)vicddt->ddt$ps_mntver_2; (void*)thisbash->ucb$l_oaltst = (void*)vicddt->ddt$ps_altstart_2; (void*)thisbash->ucb$l_qirp = (void*)vicddt->ddt$ps_pending_io; /* N.B. - altstart routine is called via JSB so we need some special linkage and a macro jacket to call out from there. */ (void*)thisbash->ucb$l_aux = (void*)vicddt->ddt$l_aux_routine; (void*)thisbash->ucb$l_cancl = (void*)vicddt->ddt$ps_cancel_2; thisbash->ucb$l_enapth = 1; thisbash->ucb$q_pthtim = 0; thisbash->ucb$l_pthios = 0; thisbash->ucb$l_ptherr = 0; /* initialize all other entries */ /* Now change the device class of the secondary path so it does not look like a disk any more. Eventually remove it from the device queue completely. */ vicucb->ucb$b_devclass = DC$_MISC; /* Hide the UCB from search routines */ vicucb->ucb$l_devchar2 |= UCB$M_HIDEUCB; /* For the present also inhibit assigning channels to the device. */ vicucb->ucb$l_sts |= UCB$M_NO_ASSIGN; } swucb->ucb$r_SW_ucb.ucb$v_valid = 1; swucb->ucb$r_SW_ucb.ucb$v_online = 1; /* Now done with mung setup. */ fork_unlock(swucb->ucb$r_SW_ucb.ucb$b_flck,savipl,SMP_RESTORE); } /* unmung subfunction. */ /* Abstract: */ /* This function undoes the mung operation, restoring drives to their */ /* original state. It must be called with the same SW and device matches */ /* as had been originally used. */ /* Expects to hold i/o mutex. */ /* Note: you only call unmung for the master path device. It clears all */ /* other references. */ /* */ /* Inputs: swucb - UCB of SW device which will control use of the path vicucb - UCB of device being added as a path controlled vicddb - DDB of device being added as a controlled path vicsb - SB of device being added " " " " mysw - index being added into the tuple (0-max). Greater than the max paths implies use next free path. Outputs: vicucb is unintercepted if this is the first path being added to the SW device. Otherwise, entry points for the path are recorded, as is its UCB address. Vicucb hiding logic is however not undone currently. The intercept is removed from the host device chain. This will work even if multiple intercepts exist, so long as all follow the format standard here. Synchronization: Takes out fork lock and returns it. Requires IO write mutex be held on call. */ void unmung(SW_UCB* swucb, UCB* vicucb, DDB* vicddb, SB* vicsb, long mysw){ int myunit; int savipl; DDT* vicddt; DDT* swddt; FDT* vicfdt; FDT* swfdt; DDT* swcpyddt; long* long1; long * long2; long * long3; /* scratch pointers */ long * long4; DPT* mydpt; PTHPTR * thisbash; SW_UCB* stepucb; PTHPTR * wrkbash; int thisunit; int kk,kkk,kkkk; SW_UCB* icucb; SW_UCB* nxtucb; long mymagic; vicddt = (DDT*)vicddb->ddb$l_ddt; swcpyddt = vicucb->ucb$l_ddt; mymagic = magic; /* The victim UCB better point at the same DDT that we have in our intercept UCB or there's an error. */ if ((long)swcpyddt != (long)&swucb->ucb$a_vicddt) return; /* be certain we can undo the "standard bash" */ fork_lock(swucb->ucb$r_SW_ucb.ucb$b_flck,(int*)&savipl); vicddt = (DDT*)vicddb->ddb$l_ddt; swcpyddt = vicucb->ucb$l_ddt; /* If we can undo std bash, then update the links and so on. */ mydpt=&driver$dpt; (char*)stepucb=(char*)vicddt - (long)offsetof(SW_UCB,ucb$a_vicddt); while ((long)stepucb->ucb$ahdofddt.DI_PPD.ucb$l_uniqid != (long)mydpt) { if ((long)stepucb->ucb$ahdofddt.DI_PPD.ucb$l_icsign != mymagic || (long)stepucb->ucb$ahdofddt.DI_PPD.ucb$l_prevddt >= 0){ fork_unlock(swucb->ucb$r_SW_ucb.ucb$b_flck,savipl,SMP_RESTORE); return; } (char *)stepucb = (char*)stepucb->ucb$ahdofddt.DI_PPD.ucb$l_prevddt - (long)offsetof(SW_UCB,ucb$a_vicddt); if ((long)stepucb >= 0){ fork_unlock(swucb->ucb$r_SW_ucb.ucb$b_flck,savipl,SMP_RESTORE); return; } } /* now we have the intercept ucb in stepucb */ if ((long)stepucb->ucb$ahdofddt.DI_PPD.ucb$l_intcddt < 0){ /* We were intercepted. Fix up the next guy in line. */ (long)nxtucb = (long)stepucb->ucb$ahdofddt.DI_PPD.ucb$l_intcddt - (long)offsetof(SW_UCB,ucb$a_vicddt); nxtucb->ucb$ahdofddt.DI_PPD.ucb$l_prevddt = stepucb->ucb$ahdofddt.DI_PPD.ucb$l_prevddt; } /* If we intercepted someone, fix the previous guy in line too. */ if ((long)stepucb->ucb$ahdofddt.DI_PPD.ucb$l_prevddt < 0){ (long)icucb = (long)stepucb->ucb$ahdofddt.DI_PPD.ucb$l_prevddt - (long)offsetof(SW_UCB,ucb$a_vicddt); /* Is what we intercepted the virgin driver ? */ if ((long)icucb != (long)vicucb) icucb->ucb$ahdofddt.DI_PPD.ucb$l_intcddt = stepucb->ucb$ahdofddt.DI_PPD.ucb$l_intcddt; } /* Now if victim's ucb ddt entry points at us, make it point at our predecessor. If if points at a successor we can leave it alone. */ if ((long)swcpyddt == (long)&stepucb->ucb$a_vicddt){ __PAL_IMB(); (long)vicucb->ucb$l_ddt = (long)stepucb->ucb$ahdofddt.DI_PPD.ucb$l_prevddt; __PAL_IMB(); swucb->ucb$l_mungd = 0; } /* Note that if we hid the UCB before, we really should unhide it. When we remove the UCB from system lists in mung, this module should be altered to add them back in. */ /* now clear the other fields of swucb that flag interception */ thisbash = &swucb->ucb$a_hstdta[0]; thisbash->ucb$l_hstucb = 0; thisbash->ucb$l_oldmv = 0; thisbash->ucb$l_oaltst = 0; thisbash->ucb$l_qirp = 0; /* N.B. - altstart routine is called via JSB so we need some special linkage and a macro jacket to call out from there. */ thisbash->ucb$l_aux = 0; thisbash->ucb$l_cancl = 0; thisbash->ucb$l_enapth = 0; thisbash->ucb$q_pthtim = 0; thisbash->ucb$l_pthios = 0; thisbash->ucb$l_ptherr = 0; /* initialize all other entries */ swucb->ucb$ahdofddt.DI_PPD.ucb$l_prevddt = 0; swucb->ucb$ahdofddt.DI_PPD.ucb$l_intcddt = 0; /* That looks like about it. */ swucb->ucb$r_SW_ucb.ucb$v_valid = 0; swucb->ucb$r_SW_ucb.ucb$v_online = 0; fork_unlock(swucb->ucb$r_SW_ucb.ucb$b_flck,savipl,SMP_RESTORE); } /* end of unmung */ /* GetSWUCB routine */ /* Abstract: */ /* This routine is called with the UCB of an intercepted device, and must locate the intercept UCB from it. It uses the fact that the intercept code puts the DDT physically into the intercept UCB to compute this but chains along in case of multiple intercepts. Inputs: inucb - UCB of device that might be intercepted Implicit inputs: Intercept block format defined here must be followed by any DDT intercepts. Outputs: UCB of SW device intercepting the device, if any. Note: This routine does not depend on SW unit; it will find the intercept so long as the intercept block scheme defined here is followed. Conversely it will fail if this scheme is broken, since it cannot reliably track intercepts back to their source unless it knows their format. */ /* Notice that this code presumes that a device is intercepted by a SWdriver unit once only, and will find that unit. */ SW_UCB* getswucb(UCB* inucb){ DPT* magicdpt; DDT* ddtp; SW_UCB* myswucb; int savipl; ddtp = inucb->ucb$l_ddt; fork_lock(inucb->ucb$b_flck,(int*)&savipl); (char*)myswucb=(char*)ddtp - (long)offsetof(SW_UCB,ucb$a_vicddt); while ((long)myswucb < 0){ if ((long)myswucb->ucb$ahdofddt.DI_PPD.ucb$l_uniqid == (long)&driver$dpt){ /* The UCB has the unique id of THIS driver, so we have found OUR UCB|| */ /* Thus we're done; return our UCB as the result. */ fork_unlock(inucb->ucb$b_flck,savipl,SMP_RESTORE); return myswucb; } if (myswucb->ucb$ahdofddt.DI_PPD.ucb$l_icsign != magic){ /* The UCB found isn;t one of ours, uses a different intercept, so return fail */ fork_unlock(inucb->ucb$b_flck,savipl,SMP_RESTORE); return 0; } if ((long)myswucb->ucb$ahdofddt.DI_PPD.ucb$l_prevddt < 0){ /* Note that even though we're trying to compute a "UCB", we actually only look at offsets within the intercept area here which should be present in any intercept UCB that uses this scheme. While this construct is a bit odd looking (it's not so odd in macro32 btw) it is actually not relying on anything except the intercept block format. */ /* Get the previous DDT. Looks like this device has been intercepted more than once. */ (char*)myswucb = (char*)myswucb->ucb$ahdofddt.DI_PPD.ucb$l_prevddt - (long)offsetof(SW_UCB,ucb$a_vicddt); } else { myswucb = 0; } } fork_unlock(inucb->ucb$b_flck,savipl,SMP_RESTORE); return 0; } /* call_orig_fdt routine */ /* Call this after an FDT intercept to call the original FDT processing where this is needed for a function. This routine follows the FDT chain back and calls the previous FDT processing. */ /* Abstract: This routine is used to find the FDT code which was intercepted by our intercept, and to call it, following the pointers to the previous DDT intercept or the original DDT to find the FDT code. Where intercepted FDT code is meant to logically perform functions ahead of the original code, and where it is necessary for the original code to be run, this routine may be called to transfer control to the original FDT code, rather than having to perform such calls separately for each FDT routine. The routine has some knowledge of the format of a Step 2 FDT table. Inputs: origirp - The IRP we are working on origpcb - PCB of process origucb - UCB of the device intercepted origccb - CCB of the channel Outputs: Original FDT code is called. Status from this returns. */ int call_orig_fdt(IRP* origirp, PCB* origpcb, UCB* origucb, CCB* origccb){ SW_UCB* myswucb; FDT* myfdt; /* long1 is a pointer to an array of pointers to function returning int */ int (*(*long1)[64])(); int (* longfcn)(); int myfcn; int mystat; mystat = 0; if ((long)(myswucb = getswucb(origucb)) < 0){ myfdt = (FDT*)myswucb->ucb$l_oldfdt; /* get the original FDT (as we intercepted)*/ (long)long1 = (long) myfdt + 2 * sizeof(long); /* Now we have the address of the original FDT routine for a step2 fdt */ /* Now get the function number and call the procedure addressed */ myfcn = origirp->irp$v_fcode; longfcn = *long1[myfcn]; mystat = longfcn(origirp, origpcb, origucb, origccb); } return (mystat); } /* fixsplit routine */ /* This routine is called at I/O post time, being passed the address of the IRP being used. It will wherever possible be called directly from fork IPL but must be prepared to fork at need. It checks for I/O status that should start mount verify (or the shdriver analogue) and starts that processing if necessary, or restores IRP context to that obtaining at entry to the switching code and completes the IRP. */ /* The auxiliary queue header is (axs*) SW_AXSHH and is initialized at unit init as a queue. */ /* Abstract: This routine gains control, generally at IPL 8 (via fast_finish), when an IRP is posted. It counts down outstanding I/O, checks for whether mount verify needs to be started (and starts it if so), pops original context back and re-posts the IRP as it would have been posted had the intercept not been present. Inputs: irp - The IRP that was posted. Implicit inputs: If the IRP is of the 7.1 type, a structure pointed to within SWDRIVER must exist to hold IRP context. Outputs: IRP is posted, MV started if appropriate, outstanding I/O counted down. */ void fixsplit(IRP* irp){ AXS * myaxsp; AXSSTK* myaxsp2; int kk,kkk,kkkk,kkkkk; /* temps */ long mystat1,mystat2; int oldipl; PTHPTR* mypath; AXS* myaxe; UCB* origucb; UCB* mastrucb; long savipl; SW_UCB* myswucb; long pid,origics,orgstat,media,flgs; long curstat, curucb; /* First find the UCB we started with. This means first checking to see if the IRP is long enough to have an internal arg stack, and then whether the context info we need is on that stack. If either is false, we must locate the context information in pool using our auxiliary information queue header which is global to this driver. If not we can get the info right out of the IRP. */ /* The irp$v_onstack flag is for longer IRPs that have the flag and signals that the context didn't fit on the IRP stack and thus a separate axs structure had to be allocated */ /* The irp$v_gotstk flag is to be added to indicate in a positive way that a stack exists. It may need to be pulled if there are too few bits left. */ pid = 0; origics = 0; orgstat = 0; origucb = 0; media = 0; /* zero context variables initially */ /* check that irp is long enough, flagged for stack, and has valid internal stack pointer. Otherwise treat as NOT an IRP with an arg stack */ if ((irp->irp$w_size <= IRP$K_LENGTH_V71) || ((irp->irp$l_stkflgs & 1) == 0) || /* (irp->irp$v_gotstk == 0) || */ ((unsigned)irp->irp$l_curcsp - (unsigned)irp > (unsigned)IRP$K_LENGTH )){ /* The info we want must be on an auxiliary structure. Therefore go get that and fix up the IRP, keeping local copies of info so we can use them in deciding things like how to synch IPL. */ myaxsp = SW_AXSHH; /* get the initial list element */ while ((long)myaxsp != (long)&SW_AXSHH){ if ((long)myaxsp->axs$l_irp == (long)irp){ /* Found our aux entry here. Grab off the values we need from it and break out of the while loop. */ origucb = (UCB*) myaxsp->axs$l_orgucb; pid = myaxsp->axs$l_pid; if (irp->irp$w_size >= IRP$K_LENGTH){ origics = myaxsp->axs$l_orgics; } media = myaxsp->axs$l_media; myswucb = (SW_UCB*)myaxsp->axs$l_swucb; orgstat = myaxsp->axs$l_orgstat; /* Now remque this entry and deallocate it; we are done looking for it. */ __PAL_REMQUEL(myaxsp,(void*)&myaxe); /* deallocate the pool now. */ kkk = exe_std$deanonpgdsiz(myaxsp,sizeof(AXS)); myaxsp = (AXS*)&SW_AXSHH; break; } else { myaxsp = (AXS*)myaxsp->axs$l_fwd; /* get next entry */ } } } else { /* The information should be in the IRP itself, so just grab it off the IRP stack. */ /* we increment the stack after fillin, so must decrement pointer to use */ irp->irp$l_curcsp = irp->irp$l_curcsp - sizeof(AXSSTK); myaxsp2 = (AXSSTK*)irp->irp$l_curcsp; origucb = (UCB*)myaxsp2->axs$l_orgucb; pid = myaxsp2->axs$l_pid; origics = myaxsp2->axs$l_orgics; media = myaxsp2->axs$l_media; myswucb = myaxsp2->axs$l_swucb; orgstat = myaxsp2->axs$l_orgstat; } /* Now restore the IRP to prior configuration including its stack */ /* irp$v_onstack must be one of these flags */ if (irp->irp$w_size >= IRP$K_LENGTH){ /* Only restore stack stuff if the IRP is long enough! */ irp->irp$l_stkflgs = origics; } /* Now we should have the original (sw unit) UCB */ /* Reset the IRP state to what it needs to be */ curstat = irp->irp$l_sts; /* Need this to handle IPL right */ curucb = (long)irp->irp$l_ucb; irp->irp$l_pid = pid; irp->irp$l_sts = orgstat; irp->irp$l_ucb = origucb; /* Get the I/O status first before restoring IRP */ mystat1 = irp->irp$l_iost1; mystat2 = irp->irp$l_iost2; /* Do NOT alter status yet, though we may need to do so if we have to enter MV */ /* irp->irp$l_media = media; */ mastrucb = origucb; #ifdef DEBUG /* Save off the i/o status of the last IRP for debug */ myswucb->ucb$l_dbgsts = mystat1; myswucb->ucb$l_irpadrp = (long)irp; #endif /* Now get to the fork level for host device if we need to. */ if ((orgstat & IRP$M_FINIPL8) == 0){ fork_lock(origucb->ucb$b_flck,(int*)&savipl); } mypath = &myswucb->ucb$a_hstdta[myswucb->ucb$l_indrct]; /* count down active IRPs now. */ if ( --myswucb->ucb$l_outstnd < 0) myswucb->ucb$l_outstnd = 0; /* maintain path pointer statistics */ mypath->ucb$q_pthtim = exe$gl_abstim; /* record time of last i/o done */ mypath->ucb$l_pthios++; /* count i/o done */ if ((mystat1 & 1) == 0)mypath->ucb$l_ptherr++; /* count errors */ /* Don't check for mount verify begin on MV's IRPs nor on IRPs from shdriver if this is a shadow set member */ if ((irp->irp$v_mvirp == 0) && ((origucb->ucb$l_devchar2 & DEV$M_SSM) == 0 || (irp->irp$v_func != IO$_PACKACK ) )){ /* Not a MV IRP */ if ((mystat1 & 16384) != 0 && (mystat1 & 1) == 1 ){ /* Here if we have failover or failback */ /* Send a message to our server process if one exists and looks active, and then complete the IRP with success. */ /* The send-message routine checks that the daemon exists and sends the message if and only if so. */ send_daemon_msg(irp,myswucb,origucb,mystat1); mystat1 = mystat1 - 16384; /* Following duplicates code below for success. */ /* If this IRP is one we changed to a medofl state, clear the flag and let it by now. We only allow one IRP to be modified like this to keep any media errors from causing hangs or looping. */ if ((long)irp == (long)myswucb->ucb$l_errirp){ myswucb->ucb$l_errirp = 0; myswucb->ucb$l_errchgwedge = 0; } /* Now store I/O status in the IRP */ irp->irp$l_iost1 = mystat1; irp->irp$l_iost2 = mystat2; /* Postprocess the IRP now */ com_std$post_nocnt(irp); if ((orgstat & IRP$M_FINIPL8) == 0){ fork_unlock(origucb->ucb$b_flck,savipl,SMP_RESTORE); } return; } else { /* not failover or failback */ /* See if we should start mount verify */ if (!$VMS_STATUS_SUCCESS(mystat1)){ /* IRP was not a success, so try an error case. */ if ((int)origucb->ucb$b_devclass == DC$_DISK && (((int)origucb->ucb$l_devchar & DEV$M_MNT) != 0) && (((int)origucb->ucb$l_devchar & DEV$M_FOR) == 0) && pid > 0){ kkk = mystat1 & 65535; /* mask off low word */ /* Since we don't know clearly if the connection is bad, instead on a set of errors, TRY to let MV start and see if the connection really is bad. */ if (myswucb->ucb$l_errchgwedge == 0 && (kkk == SS$_CTRLERR || kkk == SS$_DRVERR || kkk == SS$_DEVOFFLINE || kkk == SS$_IVSTSFLG || kkk == SS$_PATHLOST || kkk == SS$_BUS_PHASE_ERROR || kkk == SS$_LINKABORT )){ /* Store this IRP's address for later if we had none */ /* If however we're within the hysteresis time after a switch (HYSTERESIS sec) then leave the status alone and let things fail however they will. If that has happened it appears the failure couldn't be fixed by path flipping anyway so let the errors by unaltered. */ if ((myswucb->ucb$l_dwell != 0) && (myswucb->ucb$l_dwell + HYSTERESIS) > exe$gl_abstim){ if ((long)myswucb->ucb$l_errirp == 0)myswucb->ucb$l_errirp = (long)irp; myswucb->ucb$l_errchgwedge = 1; /* ... and replace the status with ss$_medofl now to let MV have a chance */ mystat1 = mystat1 - kkk + SS$_MEDOFL; } } if (origucb->ucb$v_mntverpnd){ origucb->ucb$v_mntverpnd = 0; origucb->ucb$v_mntverip = 0; /* clr mv in prog so it can start */ } irp->irp$l_media = media; /* restore media for poss. retry */ /* See if we need to start mount verify now. */ kkk = exe_std$mount_ver(mystat1, mystat2, irp, origucb); /* Note that if this is a shadowset member we must add code to test that case. */ if (!$VMS_STATUS_SUCCESS(kkk)){ /* must return here if going into MV without doing anything to the IRP */ if ((orgstat & IRP$M_FINIPL8) == 0){ fork_unlock(origucb->ucb$b_flck,savipl,SMP_RESTORE); } return; } } } /* end of test on 16384 bit */ /* Here we must process the IRP either because it was successful or because MV processing said we must. */ /* If this IRP is one we changed to a medofl state, clear the flag and let it by now. We only allow one IRP to be modified like this to keep any media errors from causing hangs or looping. */ if ((long)irp == (long)myswucb->ucb$l_errirp){ myswucb->ucb$l_errirp = 0; myswucb->ucb$l_errchgwedge = 0; } /* Now store I/O status in the IRP */ irp->irp$l_iost1 = mystat1; irp->irp$l_iost2 = mystat2; /* Postprocess the IRP now */ /* com_std$post(irp,origucb);*/ com_std$post_nocnt(irp); if ((orgstat & IRP$M_FINIPL8) == 0){ fork_unlock(origucb->ucb$b_flck,savipl,SMP_RESTORE); } return; } } else { /* MV irp or shadow packack */ /* If this IRP is one we changed to a medofl state, clear the flag and let it by now. We only allow one IRP to be modified like this to keep any media errors from causing hangs or looping. */ if ((long)irp == (long)myswucb->ucb$l_errirp){ myswucb->ucb$l_errirp = 0; myswucb->ucb$l_errchgwedge = 0; } /* Now store I/O status in the IRP */ irp->irp$l_iost1 = mystat1; irp->irp$l_iost2 = mystat2; /* Postprocess the IRP now */ com_std$post_nocnt(irp); if ((orgstat & IRP$M_FINIPL8) == 0){ fork_unlock(origucb->ucb$b_flck,savipl,SMP_RESTORE); } return; } } /* CTRL_INIT - Controller Initialization Routine */ /* */ /* This routine is used to perform controller specific initialization */ /* and is called by 1) system startup, 2) during driver loading and */ /* 3) during power failure recovery. */ /* */ /* Usage: */ /* */ /* status = ctrl_init (idb, ddb, crb) */ /* */ /* Input: */ /* idb pointer to the idb */ /* ddb pointer to the ddb */ /* crb Pointer to the crb */ /* */ /* Output: */ /* None. */ /* */ /* Return value: */ /* status SS$_NORMAL - unit was initialized successfully. */ int ctrl_init (IDB *idb, DDB *ddb, CRB *crb) { #ifdef DEBUG /* ini$brk (); */ /* Request breakpoint */ #endif return SS$_NORMAL; /* Return SUCCESS */ } /* UNIT_INIT - Unit Initialization Routine */ /* */ /* This routine is used to perform unit specific initialization */ /* and is called by 1) system startup, 2) during driver loading and */ /* 3) during power failure recovery. */ /* */ /* This routine does very little work. Its primary job is to start up */ /* the fork process that will do the bulk of the unit initialization. */ /* */ /* Usage: */ /* */ /* status = unit_init (idb, ucb) */ /* */ /* Input: */ /* idb pointer to the IDB */ /* ucb pointer to the UCB */ /* */ /* Output: */ /* None. */ /* */ /* Return value: */ /* status SS$_NORMAL - unit was initialized successfully. */ int unit_init (IDB *idb, SW_UCB *ucb) { PCB* fakepcb; DDT* myddt; if (baseucb.ucb$v_power) /* Is this power recovery ? */ return SS$_NORMAL; /* Power recovery - just exit */ baseucb.ucb$v_online = 0; /* Start with unit offline */ baseucb.ucb$v_valid = 0; ucb->ucb$ahdofddt.ucb$l_flags.lw = 0; /* Clear all the flags */ baseucb.ucb$v_template = 1; /* set as a template device */ myddt = (DDT*)baseucb.ucb$l_ddt; myddt->ddt$ps_mntver_2 = &sw_fakeformat; /* Set up and queue fork process to complete the unit initialization */ SW_AXSHH = (struct axs*)&SW_AXSHH; /* Initialize the aux structure queue header */ SW_AXSTT = (struct axs*)&SW_AXSHH; fakepcb = (PCB*)&ucb->ucb$l_fakepcb; fakepcb->pcb$l_pid = (unsigned int)&fixsplit; baseucb.ucb$l_fpc = &unit_init_fork;/* Point to fork routine address */ exe_std$primitive_fork(0,(int64)idb,(FKB *) ucb);/* Start fork process */ return SS$_NORMAL; /* Return with success */ } /* UNIT_INIT_FORK - Unit Initialization Fork Routine */ /* */ /* This is the fork routine that does the bulk of the unit */ /* initialization work. */ /* */ /* Usage: */ /* */ /* unit_init_fork ( fr3, idb, ucb) */ /* */ /* Input: */ /* fr3 Fork routine parameter (unused) */ /* idb pointer to the IDB */ /* ucb pointer to the UCB */ /* */ /* Output: */ /* None. */ /* */ /* Return value: */ /* none */ void unit_init_fork(void *fr3, IDB *idb, SW_UCB *ucb) { int status; /* Return status value */ int page_cnt; /* Number of pages to allocate */ int offset; /* PTE offset in page table */ int csr_base; /* Base CSR address */ CDDB* mycddb; UCB* scratchu2; MSCP_UCB* scratchucb; int kkk; /* scratch */ PTHPTR* pptr; /* pointer to path descriptor */ /* be sure our auxiliary structures are cleared */ ucb->ucb$ahdofddt.DI_PPD.ucb$l_uniqid = (long)&driver$dpt; ucb->ucb$ahdofddt.DI_PPD.ucb$l_icsign = magic; ucb->ucb$ahdofddt.DI_PPD.ucb$l_intcddt = 0; ucb->ucb$ahdofddt.DI_PPD.ucb$l_prevddt = 0; ucb->ucb$ahdofddt.DI_PPD.ucb$l_icpflg2.lfg2 = 0; ucb->ucb$ahdofddt.DI_PPD.ucb$l_icpflgs.lfg = 0; ucb->ucb$l_mungd = 0; (long)ucb->ucb$l_vict = 0; ucb->ucb$l_backlk = 0; ucb->ucb$l_retries = 0; (long)ucb->ucb$l_hstartio = 0; (long)ucb->ucb$l_oldfdt = 0; ucb->ucb$l_daemon = 0; ucb->ucb$l_sawsucc = 0; ucb->ucb$l_indrct = 0; /* start with 0th path */ ucb->ucb$l_mbxucb = 0; ucb->ucb$l_ptherrs = 0; /* no errs here yet */ ucb->ucb$l_errchgwedge = 0; ucb->ucb$l_errirp = 0; #ifdef DEBUG ucb->ucb$l_axsque = (struct axs*)&SW_AXSHH; #endif ucb->ucb$q_swtime = 0; ucb->ucb$l_mypost = (long*)&fixsplit; ucb->ucb$l_outstnd = 0; /* no outstanding i/o yet. */ ucb->ucb$l_mscpctrl = 0; scratchu2 = (UCB*) ucb; scratchucb = (MSCP_UCB*) ucb; ucb->ucb$l_srvbits = scratchu2->ucb$l_devchar2; if ((long)mycddb = (long)scratchucb->ucb$l_cddb < 0){ /* get initial mscp controller model info if any */ ucb->ucb$l_mscpctrl = mycddb->cddb$b_cntrlmdl; } for (kkk = 0;kkk < NUM_HOSTS; kkk++){ pptr = &ucb->ucb$a_hstdta[kkk]; (long)pptr->ucb$l_hstucb = 0; (long)pptr->ucb$l_oldmv = 0; (long)pptr->ucb$l_oaltst = 0; (long)pptr->ucb$l_qirp = 0; (long)pptr->ucb$l_cancl = 0; (long)pptr->ucb$l_aux = 0; pptr->ucb$l_enapth = 0; /* disable and null the paths initially */ pptr->ucb$q_pthtim = 0; pptr->ucb$l_pthios = 0; /* zero i/o and err counts also */ pptr->ucb$l_ptherr = 0; ucb->ucb$l_pthord[kkk] = kkk; /* initially try paths in order */ } baseucb.ucb$v_valid = 0; /* Set unit initially not valid */ baseucb.ucb$v_online = 1; /* Set the unit as ONLINE */ return; /* and return to the caller */ } /* STARTIO - Start I/O Routine */ /* */ /* This routine receives IRPs sent to SWAn: units directly which are */ /* to be sent to underlying devices. These are sent either normally or */ /* without UCB Busy synchronization as Mount Verify IRPs, depending on */ /* function modifiers, and become IO$_AVAILABLE or IO$_PACKACK packets */ /* sent to the underlying device. */ /* */ /* Input: */ /* irp Pointer to I/O request packet */ /* ucb Pointer to unit control block */ /* */ /* Output: */ /* none */ void startio(IRP *irp,UCB *ucb) { SW_UCB* scratchucb; int iost1, iost2; /* IOSB fields */ int temp; /* Temporary value */ UCB * myucb; long func; long k,kk; PTHPTR * mypath; /* We will use this entry for sending IRPs to underlying devices using ioc$initiate and the like provided everything is synchronized ok, i.e., the tuple is not in mount verify. */ scratchucb = (SW_UCB*) ucb; /* We will use the function modifier bits again to select a path to send the I/O to. This is a "fire and forget" type interface, which will set the MVIRP bit in the IRP and send it to the underlying path. */ irp->irp$v_mvirp = 1; kk = irp->irp$v_fmod; /* if ALL fcn modifier bits are set this is a NORMAL packack to the main UCB */ if (kk == (IO$M_FMODIFIERS >> IO$V_FMODIFIERS)){ /* Normal send-io-on case. */ mypath = &scratchucb->ucb$a_hstdta[0]; (long)myucb = (long)mypath->ucb$l_hstucb; if ((long)myucb >= 0){ iost1 = SS$_BADPARAM; ioc_std$reqcom(iost1,iost2,(UCB *)ucb); /* Finish I/O */ return; } irp->irp$v_func = IO$_PACKACK; /* Force this to be packack */ if ((myucb->ucb$v_bsy == 0) && (myucb->ucb$v_valid == 1 ) && (myucb->ucb$v_mntverip == 0) && (myucb->ucb$v_online == 1) && (myucb->ucb$v_dismount == 0)){ /* The UCB looks OK, so send the IRP to it. */ irp->irp$l_ucb = myucb; irp->irp$v_fmod = 0; exe_std$insioqc(irp,myucb); ucb->ucb$v_bsy = 0; /* unbusy the SW UCB */ return; } else { iost1 = SS$_BADPARAM; /* return will unbusy the sw device startio... */ ucb->ucb$l_irp = irp; /* belt 'n' suspenders */ ioc_std$reqcom(iost1,iost2,(UCB *)ucb); /* Finish I/O */ return; } } else { /* Send mvirp to some particular path */ k = irp->irp$v_fmod & 0x1f; /* K is now the index of which path to use*/ k = k % (NUM_HOSTS); mypath = &scratchucb->ucb$a_hstdta[k]; irp->irp$v_fmod = 0; (long)myucb = (long)mypath->ucb$l_hstucb; /* minimal checking here...just send the I/O off */ /* However, ensure it's available or packack */ if ((irp->irp$v_func == IO$_AVAILABLE) || (irp->irp$v_func == IO$_PACKACK)){ /* Function looks OK */ irp->irp$l_ucb = myucb; ioc_std$initiate(irp, myucb); /* Fall thru to return */ ucb->ucb$v_bsy = 0; /* unbusy the SW UCB */ } else { /* I/O is done, release channel and return information about the I/O */ iost1 = SS$_BADPARAM; ioc_std$reqcom(iost1,iost2,(UCB *)ucb); /* Finish I/O */ } return; } } /* Send_daemon_msg entry Processing: Sends a message to a preregistered server IF the server exists and the mailbox looks usable. The contents of the message are in a local structure here and it is sent via wrtmailbox. Inputs: IRP of the request SW UCB (switching driver UCB in use) Main path UCB Outputs: Message to mailbox established and pointed to within SW UCB if the mailbox and process appear valid. */ void send_daemon_msg(IRP * irp,SW_UCB* myswucb,UCB* origucb, int rtncode){ struct mbmsg { int pthidx; /* which path we are using */ int swunit; /* Unit of SW device */ int allocls; /* Allocation class of main path */ char dvcnam[12]; /* Character device name from DDB */ int mrtncode; /* Return code so we can tell where we handled this */ char nodename[12]; /* Node name from SB */ } mbxmsg; DDB* myddb; SB* mysb; long kk,kkk,kkkk; long* pcbptr; PCB* trypcb; UCB* tstucb; /* First check that the host UCB exists and things look valid */ if (myswucb->ucb$l_daemon == 0)return; if (myswucb->ucb$l_mbxucb >= 0)return; pcbptr = (long*) SCH$GL_PCBVEC; if ((long)pcbptr >= 0)return; kkk = 0; for (kk=0; kk < SCH$GL_MAXPIX; kk++){ trypcb = *(PCB**)pcbptr++; if (trypcb->pcb$l_pid == myswucb->ucb$l_daemon ){ kkk = 1; break; } } if (kkk == 0){ myswucb->ucb$l_daemon = 0; return; } /* The server process is still in the current list. Now be sure the mailbox looks sane & used */ tstucb = (UCB*)myswucb->ucb$l_mbxucb; if (tstucb->ucb$b_type != DYN$C_UCB)return; /* mbx ucb must be a ucb */ if (tstucb->ucb$v_online == 0)return; /* UCB must be online */ if (tstucb->ucb$l_refc <= 0)return; /* Somene needs to be accessing it */ if (tstucb->ucb$b_devclass != DC$_MAILBOX)return; /* must be a mailbox */ if ((tstucb->ucb$l_devchar & DEV$M_MBX) == 0)return; /* don't check ORB here because v7.2 may not use orb... */ /* The mailbox and so forth look ok, so form and send the message. */ mbxmsg.pthidx = myswucb->ucb$l_indrct; mbxmsg.swunit = (int)myswucb->ucb$r_SW_ucb.ucb$w_unit; myddb = origucb->ucb$l_ddb; /* find the DDB of master path */ memcpy(&mbxmsg.dvcnam[0],&myddb->ddb$b_name_len,12); mbxmsg.allocls = myddb->ddb$l_allocls; mbxmsg.mrtncode = rtncode; mysb = (struct _sb *)myddb->ddb$l_sb; memcpy(&mbxmsg.nodename[0],&mysb->sb$t_nodename,16); kkk = exe_std$wrtmailbox((struct _mb_ucb *)myswucb->ucb$l_mbxucb,sizeof(mbxmsg),&mbxmsg); return; } /* * Steal-Start Entry (stealstart) * * Function: * This entry replaces the primary path driver's START_IO entry * and gains control for all calls thereto, thus seeing all normal * IRPs coming into the system. (Additional entries gain control of * other driver entry points to control abnormal IRPs coming in from * altstart and the like.) * It functions as the primary switch site to switch IRPs from the * master path to whichever subordinate driver should process them * (the master or another). It also monitors I/O requests for some * specially qualified ones which are to be to subordinate particular * paths, and directs these, and monitors for MV IRPs which are then * counted and used as signals for path switching. * * Inputs: * IRP - the IRP the user has queued * UCB - The UCB of the primary path which the I/O request has come in for * * Outputs: * Control transferred as appropriate with IRP and switching UCB selected * as appropriate. This code also will generate calls to send messages * to the switching server if it exists. */ void steal_start(IRP* irp, UCB* ucb){ SW_UCB * myswucb; PTHPTR * mypath; PTHPTR * mypath0; UCB* origucb; void *(* myentry)(IRP* xirp, UCB* xucb); UCB* scratchucb; int igold; UCB* scratchucb2; MSCP_UCB* scratchucb3; UCB* scratchucb4; UCB* pathucb; DDB* scratchddb; FDT* swfdt; UCB* hstucb; FDT* genfdt; long k,kk,kkk,kkkk,kkkkk,kkkkkk,kkkkkkk; /* short term temps */ origucb = ucb; myswucb = getswucb((UCB*)ucb); #ifdef DEBUG myswucb->ucb$l_irpsts = irp->irp$l_sts; myswucb->ucb$l_irpfunc = irp->irp$l_func; myswucb->ucb$l_irpadr = (long)irp; myswucb->ucb$l_irppid = (long)irp->irp$l_pid; /* ini$brk(); */ #endif /* We should always find an intercept UCB if we've gotten to this point! It should only be able to fail if someone does an incompatible intercept and then we'll very shortly crash, leaving obvious traces of what went wrong. */ /* Because this is "not supposed to" happen leave a test out of this code for now. It is possible to follow the original DDT vector and bail out by sending I/O via that original DDT (following UCB->DDB->DDT) but with the result that switching gets silently disabled. Be noisy if someone screws us up. */ myswucb->ucb$l_backlk = (long*)ucb; /* Check for stall special case */ /* This is flagged by IRP p5 set to the address of our SW UCB */ /* If this is that special case, just leave the UCB stalled */ if ((long)myswucb == (long)irp->irp$q_qio_p5)return; /* Check that the IRP may validly be sent. This means that either the function must be one of those the MSCP server routinely passes or that the FDT address which would be used for the function matches the master path's FDT address that will have been used here. If it does not, we will return an illegal function error right here to block any problems. */ /* For simplicity here, the SW FDT will reflect functions that MSCP would allow (except for the addition of IO$_PHYSICAL) */ k = irp->irp$v_fmod; k = k & 0xffffffe0; /* 6 bits for function */ if (k == ((IO$M_FMODIFIERS>>IO$S_FCODE) & 0xffffffe0)){ if (myswucb->ucb$l_indrct != 0 && (k != ((IO$M_FMODIFIERS>>IO$S_FCODE) & 0xffffffe0))){ /* master path I/O is always OK, but we must check all other. */ /* However, if this is an abstraction breaking IRP don't check. The server must get these right! */ kk = irp->irp$v_fcode; kk = kk & 0x3f; /* be sure no sign extending is done */ mypath = &myswucb->ucb$a_hstdta[myswucb->ucb$l_indrct]; /* Is the function one that SWdriver considers OK and thus should be ok for MSCP? */ scratchucb = (UCB*) myswucb; swfdt = scratchucb->ucb$l_ddt->ddt$ps_fdt_2; if (kk == IO$_PHYSICAL || swfdt->fdt$ps_func_rtn[kk] == EXE$ILLIOFUNC){ /* The function is format, or is one that is not used for mscp, so we will see if the FDT that WAS used is the one that the underlying driver path we will use was expecting to have been used. If NOT, we will generate an ILLIOFUNC return right here. */ mypath0 = &myswucb->ucb$a_hstdta[0]; /* Find the two FDT tables involved */ scratchucb2 = (UCB*)mypath0->ucb$l_hstucb; swfdt = scratchucb2->ucb$l_ddt->ddt$ps_fdt_2; scratchucb2 = (UCB*)mypath->ucb$l_hstucb; genfdt = scratchucb2->ucb$l_ddt->ddt$ps_fdt_2; if (swfdt->fdt$ps_func_rtn[kk] != genfdt->fdt$ps_func_rtn[kk]){ /* FDT addresses do NOT match, and it is essential they should. So junk this I/O before it can do any damage||! */ ucb->ucb$l_irp = irp; ioc_std$reqcom(SS$_ILLIOFUNC,0,ucb); return; } } } } /* Be sure the IRP is not a mount verify IRP */ if (irp->irp$v_mvirp == 0){ /* master path I/O is always OK, but we must check all other. */ /* However, if this is an abstraction breaking IRP don't check. The server must get these right! */ kk = irp->irp$v_fcode; kk = kk & 0x3f; /* be sure no sign extending is done */ mypath = &myswucb->ucb$a_hstdta[myswucb->ucb$l_indrct]; /* Is the function one that SWdriver considers OK and thus should be ok for MSCP? */ scratchucb = (UCB*) myswucb; swfdt = scratchucb->ucb$l_ddt->ddt$ps_fdt_2; if (myswucb->ucb$l_indrct != 0){ if (kk == IO$_PHYSICAL || swfdt->fdt$ps_func_rtn[kk] == EXE$ILLIOFUNC){ /* The function is format, or is one that is not used for mscp, so we will see if the FDT that WAS used is the one that the underlying driver path we will use was expecting to have been used. If NOT, we will generate an ILLIOFUNC return right here. */ mypath0 = &myswucb->ucb$a_hstdta[0]; /* Find the two FDT tables involved */ scratchucb2 = (UCB*)mypath0->ucb$l_hstucb; swfdt = scratchucb2->ucb$l_ddt->ddt$ps_fdt_2; scratchucb2 = (UCB*)mypath->ucb$l_hstucb; genfdt = scratchucb2->ucb$l_ddt->ddt$ps_fdt_2; if (swfdt->fdt$ps_func_rtn[kk] != genfdt->fdt$ps_func_rtn[kk]){ /* FDT addresses do NOT match, and it is essential they should. So junk this I/O before it can do any damage||! */ ucb->ucb$l_irp = irp; ioc_std$reqcom(SS$_ILLIOFUNC,0,ucb); return; } } } /* First test for "special" I/Os that should go to particular paths breaking the abstraction. These have all but low 5 bits of func modifiers set (assumes 32 subpaths max) and the low fcn mfy bits flag which path to use. */ k = irp->irp$v_fmod; k = k & 0xffffffe0; if (k == ((IO$M_FMODIFIERS>>IO$S_FCODE) & 0xffffffe0)){ /* I/O should be routed to one of the subordinate paths. */ k = irp->irp$v_fmod & 0x1f; /* K is now the index of which path to use*/ /* Protect against num_hosts being less than 1f hex */ k = k % (NUM_HOSTS); irp->irp$v_fmod = 0; /* Clear the modifiers for underlying paths */ mypath = &myswucb->ucb$a_hstdta[k]; /* mypath is now the path pointer we should use. Send I/O there. Note however that if k==0, we special case to avoid recursion. */ /* fix the IRP up to send there first. */ /* Do this in one place for simpler understanding */ fixirp(irp,myswucb,k); if (k != 0){ ucb->ucb$v_bsy = 0; /* i/o to a secondary path */ exe_std$insioqc(irp,(UCB*)mypath->ucb$l_hstucb); return; } else { /* i/o to main path */ (long)myentry = (long)myswucb->ucb$l_hstartio; myentry(irp,ucb); return; } } /* Test for the golden IRP which must NOT go to anything except at most to DKdriver. */ /* Note the fixirp test is only for path 0 so do the test completely here.*/ igold=0; if ((irp->irp$l_svapte == 0) && (irp->irp$l_func == 0) && (irp->irp$l_iost1 == 0) && (irp->irp$q_qio_p1 == 0) && ((long)irp->irp$l_pid < 0)){ /* looks like the golden IRP, which should just be junked. */ /* If we are sending to a SCSI driver let it by. Otherwise junk it. */ igold=1; mypath = &myswucb->ucb$a_hstdta[myswucb->ucb$l_indrct]; pathucb = (UCB*)mypath->ucb$l_hstucb; if (((pathucb->ucb$l_devchar2 & DEV$M_SCSI) == 0 ) || ((pathucb->ucb$l_devchar2 & DEV$M_MSCP) != 0 )){ /* The path we're going to looks like NOT a SCSI path, so don't send this on anywhere. */ (long)irp->irp$l_ioqfl = 0; /* dk wants this zeroed to free it */ if (__PAL_REMQUEL(ucb->ucb$l_ioqfl,irp) >= 0){ ioc_std$initiate(irp,ucb); } return; } } /* Not special case I/O to particular path, so everything else here follows the abstraction that there is one device and several paths one of which is used at a time. */ /* However do NOT treat the golden IRP, if seen, as meaning we saw a non-mvirp. Note that for a SCSI disk it would get sent thru. */ if(igold == 0){ myswucb->ucb$l_errchgwedge = 0; myswucb->ucb$l_retries = 0; /* Flag we saw something that was NOT a mvirp */ myswucb->ucb$l_sawsucc = 1; } /* myswucb->ucb$l_outstnd++; */ #ifdef DEBUG myswucb->ucb$l_ngfunc = (long)irp->irp$l_func; #endif fixirp(irp,myswucb,k = myswucb->ucb$l_indrct); /* fix up IRP for path */ if (k != 0){ /* i/o to a secondary path */ /* Unbusy main path at this point too. */ ucb->ucb$v_bsy = 0; mypath = &myswucb->ucb$a_hstdta[myswucb->ucb$l_indrct]; exe_std$insioqc(irp,(UCB*)mypath->ucb$l_hstucb); return; } else { /* i/o to main path */ (long)myentry = (long)myswucb->ucb$l_hstartio; myentry(irp,ucb); return; } } else { /* mount verify IRP, presumably not synch'd by ucb busy */ /* Here, note, we must check for packack and do path switch if that is what is called for||! */ /* Run the path check stuff first */ if (irp->irp$v_fcode == IO$_PACKACK){ myswucb->ucb$l_sawsucc = 0; #define MIN_RETRIES 3 if (myswucb->ucb$l_retries++ > MIN_RETRIES){ /* */ /* */ /* */ /* SWITCH PATH CODE */ /* */ /* */ /* */ /* We have retried too often, BUT be sure no dwell time limit exists and don't switch if in the dwell time. */ if ((myswucb->ucb$l_dwell == 0) || ((unsigned)(myswucb->ucb$l_dwell + HYSTERESIS) < (unsigned)exe$gl_abstim)){ /* OK, we're not in a dwell interval and have counted enough packacks to flip to a next path. Do so if we can. */ myswucb->ucb$l_dwell = exe$gl_abstim; myswucb->ucb$l_retries = 0; myswucb->ucb$l_outstnd = 0; /* rezero outstanding at switch */ /* note that we cannot rezero outstanding I/O if we switch dynamically in the future sometime, but this guards against erroneous busy indicators if something goes REALLY wedged on one path. */ /* now find a path if possible */ kk = (myswucb->ucb$l_indrct ); /* current path */ /* the indirection array pthord should hold the desired order of trials. */ /* Find where in the pthord array we currently are. */ kkk = -1; for (k=0; k< NUM_HOSTS; k++){ if (myswucb->ucb$l_pthord[k] == kk){ kkk = k; break; } } /* index "kkk" is where we are on the old path in the path order select list. */ kk = -1; kkkkk = kkk; for (k=0; k< NUM_HOSTS; k++){ kkk = (kkk+1) % NUM_HOSTS; /* look at the next path */ kkkk = myswucb->ucb$l_pthord[kkk]; /* get the path index */ mypath = &myswucb->ucb$a_hstdta[kkkk]; if (mypath->ucb$l_enapth != 0){ /* Looks like we have a usable path now, finally. Therefore switch to it. */ /* Don't switch to an offline disk though!! */ hstucb = (UCB*) mypath->ucb$l_hstucb; if ((long)mypath->ucb$l_hstucb < 0 && hstucb->ucb$v_online == 1){ myswucb->ucb$l_indrct = kkkk; kk = kkkk; break; } } } /* end loop over enabled paths */ /* If the enabled list is empty, something is really wedged so as a last ditch attempt to go again, try switching while ignoring the enable bits. */ if (kk < 0){ kkk = kkkkk; for (k=0; k< NUM_HOSTS; k++){ kkk = (kkk+1) % NUM_HOSTS; /* look at the next path */ kkkk = myswucb->ucb$l_pthord[kkk]; /* get the path index */ mypath = &myswucb->ucb$a_hstdta[kkkk]; if ((long)mypath->ucb$l_hstucb < 0){ /* Looks like we have a usable path now, finally. Therefore switch to it. */ /* Do however be certain it is online. */ hstucb = (UCB*) mypath->ucb$l_hstucb; if (hstucb->ucb$v_online == 1){ myswucb->ucb$l_indrct = kkkk; kk = kkkk; break; } } } } /* end of hunt ignoring enable/disable */ /* Is the new path local? If so, allow MSCP etc. serving of the path. */ /* A path being local gets tested much as the mscp server does it except for this bit...should become exactly the same once we get a new bit.*/ scratchucb3 = (MSCP_UCB*)mypath->ucb$l_hstucb; scratchucb4 = (UCB*)mypath->ucb$l_hstucb; scratchddb = (DDB*)scratchucb4->ucb$l_ddb; kkkkkk = scratchucb4->ucb$l_devchar2; /* 6 k's */ /* here kkkkkk (6 k's) is the devchar2 field of the underlying path */ if (myswucb->ucb$l_indrct == 0)kkkkkk = myswucb->ucb$l_srvbits; /* Initially disallow serving */ ucb->ucb$l_devchar2 |= DEV$M_SRV; /* Now if the secondary path is NOT an MSCP one, be sure the main path is not marked MSCP either. If this chosen path IS the primary, recall the original cddb value though. */ /* Set up the MSCP controller model the way it should be to reflect the path chosen. */ /* NOTE add some logic for QIO server case here */ if ((long)scratchucb3->ucb$l_cddb < 0){ /* By default clear the ctrlmdl field if it looks like a CDDB is there even if this isn't really an MSCP path. */ scratchucb3->ucb$l_cddb->cddb$b_cntrlmdl = 0; } /* Now if the path is marked MSCP, set the controller model appropriately. The idea is the MSCP server will otherwise normally try to serve UCBs that look like SCSI disks, but if the main path is an MSCP path we need to make it look local and serveable if the real path we're using is OK. */ if (((scratchucb4->ucb$l_devchar2 & DEV$M_MSCP) != 0) && ((long)scratchucb3->ucb$l_cddb < 0)){ /* looks like we have a cddb */ kkkkkkk = scratchucb3->ucb$l_cddb->cddb$b_cntrlmdl; /* 7 k's */ if (myswucb->ucb$l_indrct == 0)kkkkkkk = myswucb->ucb$l_mscpctrl; /* now kkkkkkk (7 k's) is the ctrl model to use, fill 'er in. */ /* Thus if it fills in an emulator value that reflects the path we're on and conversely. */ scratchucb3->ucb$l_cddb->cddb$b_cntrlmdl = kkkkkkk; } /* end of dev$m_mscp test */ /* See if the path should be served, testing the stuff the MSCP server does. */ if (((kkkkkk & /* 6 k's */ (DEV$M_NOCLU|DEV$M_SRV|DEV$M_VRT)) == 0) && ((scratchddb->ddb$l_allocls == clu$gl_allocls) || (scratchddb->ddb$v_pac != 0))){ /* Add more tests here like the MSCP server (for now; be like QIO server later too.) */ /* Here clear the "mscp served" bit to sllow mscp serving */ ucb->ucb$l_devchar2 &= ~DEV$M_SRV; } /* The path is now the best we can find, even if it is just the original one again. */ /* Tell the server we just switched. */ send_daemon_msg(irp,myswucb,origucb,1); /* That is it for path swapping. Now merge to handle sending the I/O on. */ } } } /* At this point we have a MVIRP, of some kind, not necc. a packack. Send it to the proper path, using ioc$initiate & friends since this isn't synch'd by the UCB busy bit. */ fixirp(irp,myswucb,k = myswucb->ucb$l_indrct); /* fix up IRP for path */ /* myswucb->ucb$l_outstnd++;*/ if (k != 0){ /* i/o to a secondary path */ mypath = &myswucb->ucb$a_hstdta[myswucb->ucb$l_indrct]; ioc_std$initiate(irp,(UCB*)mypath->ucb$l_hstucb); return; } else { /* i/o to main path */ (long)myentry = (long)myswucb->ucb$l_hstartio; myentry(irp,ucb); return; } } } /* fixirp - * * Abstract: * Customizes an IRP for the path it will be using and saves * pre-existing fields that are changed to either an IRP context * stack or to an auxiliary structure it allocates, links in, and * fills in. * * Inputs: * irp - The IRP to alter * myswucb - The UCB of the SW device being used * npath - The path index currently in use * * Outputs: * none */ void fixirp(IRP* irp, SW_UCB* myswucb, int npath){ int k,kk,kkk,kkkk,kkkkk; long lk; PTHPTR * mypath; AXS * myaxs; UCB* orgucb; AXSSTK * myaxsstk; char * poolp; mypath = &myswucb->ucb$a_hstdta[npath]; /* Do nothing if the IRP is already altered, as flagged by having its irp$l_pid address pointing at fixsplit. */ if (irp->irp$l_pid == (long) &fixsplit )return; /* DKdriver has a habit of using a "golden" IRP whose only purpose is to ensure its busy bit is not set. We could wind up seeing this IRP in our intercept if we intercept a DK unit, but this is not a real IRP and really needs to just be junked. It gets junked inside DK startio if sent there. Now, it has a peculiar structure in that it is physically located inside the DK UCB. We don't really want to process it, but rather just continue to look for work, so if we find this has gotten in, don't alter it but send it by. Don't count it either. Recognize it by seeing if the IRP is physically inside the master UCB and has svapte=0, func=0. If we aren't along the main path, this busy bit stuff happens below our level and can be ignored...so only do the test if npath==0. */ if (npath == 0){ orgucb = (UCB*)mypath->ucb$l_hstucb; /* orgucb is the UCB address of the prime path UCB, so we can check the IRP to see if it's inside... */ if ((long)irp->irp$l_svapte == 0 && (long)irp->irp$l_func == 0){ lk = orgucb->ucb$w_size; lk = lk + (long)orgucb; if (((unsigned)irp > (unsigned)orgucb) && ((unsigned)irp < (unsigned)lk)){ /* This looks like the golden IRP. Just junk it by returning with no mods to the IRP, leaving it alone to be junked inside dkdriver. */ return; } } } /* Anything that is NOT an IRP is unknown to us, so ignore it. However allow IRPEs too just in case. */ if ((irp->irp$b_type != DYN$C_IRP) && (irp->irp$b_type != DYN$C_IRPE)) return; myswucb->ucb$l_outstnd++; /* Now see whether we can use the IRP's internal argument stack to hold our context information. If so, we will use that. However if this cannot be done we must now allocate an axs structure to keep the old IRP information we must replace. */ if ((irp->irp$w_size <= IRP$K_LENGTH_V71) || /* (irp->irp$v_gotstk == 0) || */ /* (irp->irp$v_onstack == 0) || */ ((unsigned)irp->irp$l_curcsp - (unsigned)irp > ((unsigned)IRP$K_LENGTH - (unsigned)sizeof(AXSSTK)) )){ /* The IRP either is an old type one too short for a stack, or it has a stack, but the stack is too short for a new intercept to be added to its context, or its stack pointer cell is invalid (ie, doesn't point to the same IRP area if it points to anything at all.) Therefore we will need to allocate an axs structure to hold our context. Otherwise we'll put it into an axsstk structure, which is a bit shorter. */ k = exe_std$alononpaged(sizeof(AXSSTK),&kk,&poolp); if (!$VMS_STATUS_SUCCESS(k))return; (char*)myaxs = (char*)poolp; myaxs->axs$w_size = kk; myaxs->axs$b_type = DYN$C_MISC; myaxs->axs$l_irp = (long *)irp; myaxs->axs$l_orgucb = (long)irp->irp$l_ucb; myaxs->axs$l_pid = irp->irp$l_pid; myaxs->axs$l_orgstat = irp->irp$l_sts; myaxs->axs$l_media=irp->irp$l_media; myaxs->axs$l_swucb=(long)myswucb; #ifdef DEBUG myaxs->axs$l_func = (long)irp->irp$l_func; #endif if(irp->irp$w_size >= IRP$K_LENGTH){ /* Save old arg sp too if irp is long enough */ myaxs->axs$l_orgics = (long)irp->irp$l_curcsp; } /* If the IRP is one that is of the sort that HAS a stack but where the stack may have overflowed, set flags appropriately within it. The irp$v_onstack flag should be set to 1 on irp creation like gotstk but we need to clear it if the irp stack fills. */ myaxs->axs$l_flgs = 0; if ((irp->irp$w_size >= IRP$K_LENGTH) && /* (irp->irp$v_gotstk == 1) && */ ((unsigned)irp->irp$l_curcsp - (unsigned)irp < ((unsigned)IRP$K_LENGTH))){ /* looks like a valid stack-equipped IRP so set its flags too */ myaxs->axs$l_flgs = irp->irp$l_stkflgs; /* irp->irp$l_stkflgs = irp->irp$l_stkflgs & 0xfffffffe; */ irp->irp$l_stkflgs = irp->irp$l_stkflgs & 0xfffffffe; /* Turn off lo bit for no stack */ } /* Now put the axs structure on its queue so we can make the irp right for the new path next. */ __PAL_INSQUEL(&SW_AXSHH,myaxs); } else { /* need an axsstk since this seems to be a new IRP */ myaxsstk = irp->irp$l_curcsp; irp->irp$l_curcsp = irp->irp$l_curcsp + sizeof(AXSSTK); myaxsstk->axs$l_irp = irp; myaxsstk->axs$l_orgucb = irp->irp$l_ucb; myaxsstk->axs$l_pid = irp->irp$l_pid; myaxsstk->axs$l_orgics = (long)myaxsstk; myaxsstk->axs$l_orgstat = irp->irp$l_sts; myaxsstk->axs$l_media = irp->irp$l_media; myaxsstk->axs$l_flgs = irp->irp$l_stkflgs; myaxsstk->axs$l_swucb = (long)myswucb; irp->irp$l_stkflgs |= 1; /* Now the information is saved in either case. */ } /* At this point we must make the IRP usable in the new path and ensure we can get control back. */ (DT_UCB*)irp->irp$l_ucb = mypath->ucb$l_hstucb; /* Make IRP usable on the path */ irp->irp$l_pid = (unsigned int)&fixsplit; /* Arrange to see it at postprc time */ if ((irp->irp$v_mvirp == 0) && (myswucb->ucb$ahdofddt.DI_PPD.ucb$l_icpflgs.pa.ucb$v_fi8ok == 1) && (irp->irp$v_shdio == 0) ){ /* The IRP looks OK to do IPL 8 local postprocessing with,so do so. */ irp->irp$v_fast_finish = 1; irp->irp$v_finipl8 = 1; } irp->irp$v_swapio = 0; /* Don't let MV start below us. We need to know. */ irp->irp$v_pagio = 0; irp->irp$v_srvio = 0; /* that's it. */ return; } /* Steal mount verify entry point STEAL_MOUNT_VER (irp, ucb) * * This routine gains control when mount verify is being started or * stopped (at start, the IRP entry is filled in; at MV end the IRP * argument is null). On MV start it is expected to get the IRP into * the right UCB queue, and on MV end it is expected to start the first * IRP on the queue. * * Inputs: * IRP or 0 - the IRP to be stalled, or 0 if MV is over * UCB - the UCB of the device concerned. * * Outputs: * IRP queued or I/O restarted * * Processing: * This code will call the underlying routines. On MV start it * will call underlying MV start, then if the path is an alternate * one will move the IRPs to the master queue in order. On MV end * it will move IRPs to the appropriate device queue (processing them * first) and call underlying MV end to restart I/O. */ void steal_mount_ver(IRP* irp, UCB* ucb){ SW_UCB * myswucb; PTHPTR * mypath; PTHPTR * mastpth; int keepgoin; int isgold; UCB* mastucb; IRP* qirp; IRP* qirp2; UCB* myscrucb; /* mventry is pointer to function returning int */ int (*mventry)(); long k,kk,kkk,kkkk,kkkkk; /* short term temps */ int npath; myswucb = getswucb((UCB*)ucb); npath = myswucb->ucb$l_indrct; mypath = &myswucb->ucb$a_hstdta[npath]; myscrucb = (UCB*)mypath->ucb$l_hstucb; mastpth = &myswucb->ucb$a_hstdta[0]; mastucb = (UCB*)mastpth->ucb$l_hstucb; /* See if this is begin-MV or end-MV */ if ((long)irp != 0){ /* MV beginning */ /* Call the underlying MV routine first */ kk = SS$_BADPARAM; (long)mventry = (long)mypath->ucb$l_oldmv; if(((long)mypath->ucb$l_oldmv) < 0) kk = mventry(irp, ucb); /* Now if we're using anything other than the primary path, get any IRP that is on the secondary's path and put it on the primary's path, AHEAD of whatever else may be there. */ if (npath != 0){ while(((long)myscrucb->ucb$l_ioqfl != (long)&myscrucb->ucb$l_ioqfl) && (kk = __PAL_REMQUEL(myscrucb->ucb$l_ioqbl, (void*)&qirp) >= 0)) { unfixirp(qirp,myswucb,npath); /* fix up IRP for path */ __PAL_INSQUEL(ucb->ucb$l_ioqfl,qirp); } } } else { /* MV end */ /* get IRPs onto the right queue */ if (npath != 0){ keepgoin = 1; while((keepgoin == 1) && ((long)ucb->ucb$l_ioqfl != (long)&ucb->ucb$l_ioqfl) && (kk = __PAL_REMQUEL(ucb->ucb$l_ioqbl,(void*)&qirp) >= 0)) { #ifdef DEBUG /* Record the addresses of the last 2 IRPs sent through. */ myswucb->ucb$l_mveirp[1] = myswucb->ucb$l_mveirp[0]; myswucb->ucb$l_mveirp[0] = (long)qirp; #endif isgold = 0; /* If this was the "golden IRP" we don't want to pass it on. Rather, junk it.*/ /* Test for the golden IRP which must NOT go to anything except at most to DKdriver. */ /* Note the fixirp test is only for path 0 so do the test completely here.*/ /* The "golden" IRP is almost all zeroes, so use that to find it. */ /* It will have anegative PID tho... */ if ((qirp->irp$l_svapte == 0) && (qirp->irp$l_func == 0) && (qirp->irp$l_iost1 == 0) && (qirp->irp$q_qio_p1 == 0) && ((long)qirp->irp$l_pid < 0)){ /* looks like the golden IRP, which should just be junked. */ /* If we are sending to a SCSI driver let it by. Otherwise junk it. */ if (((myscrucb->ucb$l_devchar2 & DEV$M_SCSI) == 0 ) || ((myscrucb->ucb$l_devchar2 & DEV$M_MSCP) != 0 )){ /* The path we're going to looks like NOT a SCSI path, so don't send this on anywhere. */ (long)qirp->irp$l_ioqfl = 0; /* dk wants this zeroed to free it */ isgold = 1; } } if (isgold == 0){ fixirp(qirp,myswucb,npath); /* fix up IRP for path */ __PAL_INSQUEL(myscrucb->ucb$l_ioqfl, qirp); /* keepgoin = 0; */ } } } ucb->ucb$v_mntverip = 0; ucb->ucb$v_mntverpnd = 0; /* Ensure master AND subordinate UCBs MV bits are cleared */ mastucb->ucb$v_mntverip = 0; mastucb->ucb$v_mntverpnd = 0; mastucb->ucb$v_online = 1; /* MSCP fiddles with online bit. Try to compensate. */ myscrucb->ucb$v_mntverip = 0; myscrucb->ucb$v_mntverpnd = 0; kk = SS$_BADPARAM; (long)mventry = (long)mypath->ucb$l_oldmv; #ifdef DEBUG /* If the path underneath is mscp then look for busy indications. */ if ((myscrucb->ucb$l_devchar2 & DEV$M_MSCP) != 0 ){ if (myscrucb->ucb$w_rwaitcnt != 0){ myswucb->ucb$l_rwt[0] = myscrucb->ucb$w_rwaitcnt; myswucb->ucb$l_rwt[1] = myscrucb->ucb$l_sts; } } #endif if(((long)mypath->ucb$l_oldmv) < 0) kk = mventry(irp, ucb); return; } } /* unfixirp - * * Abstract: * Un-Customizes an IRP for the path it will be using and restores * pre-existing fields from where fixirp saved them. * * Inputs: * irp - The IRP to alter * myswucb - The UCB of the SW device being used * npath - The path index currently in use * * Outputs: * none */ void unfixirp(IRP* irp, SW_UCB* myswucb, int npath){ int k,kk,kkk,kkkk,kkkkk; PTHPTR * mypath; AXS * myaxs; AXS * myaxsp; AXSSTK * myaxsstk; char * poolp; /* Do nothing unless the IRP is already altered, as flagged by having its irp$l_pid address pointing at fixsplit. */ if (irp->irp$l_pid != (long) &fixsplit )return; if ( --myswucb->ucb$l_outstnd < 0) myswucb->ucb$l_outstnd = 0; npath = myswucb->ucb$l_indrct; mypath = &myswucb->ucb$a_hstdta[npath]; /* Now see whether we used the IRP's internal argument stack to hold our context information. If so, we will use that. However if this cannot be done we must now allocate an axs structure to keep the old IRP information we must replace. */ if ((irp->irp$w_size <= IRP$K_LENGTH_V71) || /* (irp->irp$v_gotstk == 0) || */ ((irp->irp$l_stkflgs & 1) == 0) || ((unsigned)irp->irp$l_curcsp - (unsigned)irp > ((unsigned)IRP$K_LENGTH) )){ /* The IRP either is an old type one too short for a stack, or it has a stack, but the stack was too short for a new intercept to be added to its context, or its stack pointer cell is invalid (ie, doesn't point to the same IRP area if it points to anything at all.) Therefore we will need to allocate an axs structure to hold our context. Otherwise we'll put it into an axsstk structure, which is a bit shorter. */ /* The info we want must be on an auxiliary structure. Therefore go get that and fix up the IRP, keeping local copies of info so we can use them in deciding things like how to synch IPL. */ myaxsp = SW_AXSHH; /* get the initial list element */ while ((long)myaxsp != (long)&SW_AXSHH){ if ((long)myaxsp->axs$l_irp == (long)irp){ /* Found our aux entry here. Grab off the values we need from it and break out of the while loop. */ irp->irp$l_sts = myaxsp->axs$l_orgstat; irp->irp$l_pid = myaxsp->axs$l_pid; irp->irp$l_ucb = (UCB*)myaxsp->axs$l_orgucb; irp->irp$l_media = myaxsp->axs$l_media; if (((unsigned)irp->irp$l_curcsp - (unsigned)irp) <= ((unsigned)IRP$K_LENGTH) && (irp->irp$w_size >= IRP$K_LENGTH_V71) ) irp->irp$l_stkflgs = myaxsp->axs$l_flgs; /* Now remque this entry and deallocate it; we are done looking for it. */ __PAL_REMQUEL(myaxsp,&myaxs); /* deallocate the pool now. */ kkk = exe_std$deanonpgdsiz(myaxsp,sizeof(AXS)); (long)myaxsp = (long)&SW_AXSHH; break; } else { myaxsp = (AXS*)myaxsp->axs$l_fwd; /* get next entry */ } } } else { /* need an axsstk since this seems to be a new IRP */ irp->irp$l_curcsp = irp->irp$l_curcsp - sizeof(AXSSTK); myaxsstk = irp->irp$l_curcsp; irp->irp$l_sts = myaxsstk->axs$l_orgstat; irp->irp$l_pid = myaxsstk->axs$l_pid; irp->irp$l_ucb = (UCB*)myaxsstk->axs$l_orgucb; irp->irp$l_media = myaxsstk->axs$l_media; if (((unsigned)irp->irp$l_curcsp - (unsigned)irp) <= ((unsigned)IRP$K_LENGTH) && (irp->irp$w_size >= IRP$K_LENGTH_V71) ) irp->irp$l_stkflgs = myaxsstk->axs$l_flgs; /* Now the information is unsaved in either case. */ } /* that's it. */ } /* steal_altstart */ /* Abstract: */ /* This routine gains control whenever an altstart call is made on the main path. Its sole function is to count the I/O up and ensure it gets to the right disk underlying. Because altstart is not controlled by UCB busy, it will call the underlying altstart entries directly and not touch busy. */ /* Inputs: irp IRP to be sent to underlying alt start ucb UCB of prime path Outputs: Altstart is called on the appropriate underlying device path and outstanding I/O counted up (and it gets counted down at fixsplit). */ void steal_altstart(IRP* irp, UCB* ucb){ SW_UCB * myswucb; PTHPTR * mypath; UCB* myscrucb; void *(*myentry)(); int npath; int savipl; long k,kk,kkk,kkkk,kkkkk; /* short term temps */ myswucb = getswucb((UCB*)ucb); npath = myswucb->ucb$l_indrct; mypath = &myswucb->ucb$a_hstdta[npath]; myscrucb = (UCB*)mypath->ucb$l_hstucb; if (npath != 0)fork_lock(myscrucb->ucb$b_flck, &savipl); if ((long)mypath->ucb$l_oaltst < 0){ /* Note we must count up IRPs and fix the IRPs too. */ /* myswucb->ucb$l_outstnd++; */ fixirp(irp, myswucb, npath); (long)myentry = (long)mypath->ucb$l_oaltst; myentry(irp, ucb); } if (npath != 0)fork_unlock(myscrucb->ucb$b_flck, savipl,SMP_RESTORE); return; } /* Steal_pending */ /* Abstract: */ /* This entry ensures that pending I/O is dispatched by the underlying driver correctly. We won't edit the IRP here at all, just leave it for later */ /* NOTE this entry requires a NON-STANDARD CALL!!! since R5 at entry is assumed to be the UCB, otherwise unavailable!!!! */ /* Inputs: Queue header, IRP, UCB Outputs: The IRP is put onto the queue for the device by calling the appropriate underlying pending_io entry point, with returns from the underlying driver. Additional logic exists in some drivers (fastpath) to decide at times not to queue the IRP so this routine is merely a switch. */ #pragma linkage pendlnk = (parameters (r16, r17, r5), result (r0)) #pragma use_linkage pendlnk (steal_pending) int steal_pending(IRP* qhdr, IRP* irp, UCB* ucb){ SW_UCB * myswucb; PTHPTR * mypath; UCB* myscrucb; int npath; int savipl; int (*myentry)(); long kk; /* short term temps */ myswucb = getswucb((UCB*)ucb); npath = myswucb->ucb$l_indrct; mypath = &myswucb->ucb$a_hstdta[npath]; kk = SS$_NORMAL; myscrucb = (UCB*)mypath->ucb$l_hstucb; if (npath != 0)fork_lock(myscrucb->ucb$b_flck, &savipl); if ((long)mypath->ucb$l_oaltst < 0){ (long)myentry = (long)mypath->ucb$l_qirp; /* irp$l_ucb will be filled in when this IRP gets to start-io */ kk = myentry(qhdr, irp); } if (npath != 0)fork_unlock(myscrucb->ucb$b_flck, savipl,SMP_RESTORE); return (kk); } /* Steal_Aux_Routine */ /* Abstract: */ /* This entry is called by JSB, and needs both nonstandard call pragmas and to call glue to call the outbound routine correctly. On input R3 is its CDDB address and no IRP is to be found. */ /* We merely dispatch to the right underlying routine here. This entry is used in DUdriver to "go look for another MSCP path" just before MV starts. */ /* Inputs: CDDB pointer Outputs: Auxiliary processing is called in the correct (ie, active) underlying path. This processing is generally used to tell MSCP to look for another path prior to starting mount verify. The path is called here via some macro "glue" because the call convention does not work with C. If the underlying path had no auxiliary entry, this entry just returns. */ #pragma linkage auxlnk = (parameters (r3), result (r3)) #pragma use_linkage auxlnk (steal_aux_routine) int steal_aux_routine(char* cddb){ UCB* myucb; SW_UCB * myswucb; PTHPTR * mypath; int npath; int savipl; UCB* myscrucb; long kk; /* short term temps */ (char*) myucb = cddb + offsetof(CDDB,cddb$l_ucbchain) - offsetof(MSCP_UCB,ucb$l_cddb_link); myswucb = getswucb((UCB*)myucb); npath = myswucb->ucb$l_indrct; mypath = &myswucb->ucb$a_hstdta[npath]; kk = SS$_NORMAL; myscrucb = (UCB*)mypath->ucb$l_hstucb; if (npath != 0)fork_lock(myscrucb->ucb$b_flck, &savipl); if ((long)mypath->ucb$l_oaltst < 0){ kk = call_macro_pass_r3((int)mypath->ucb$l_aux,cddb); } if (npath != 0)fork_unlock(myscrucb->ucb$b_flck, savipl,SMP_RESTORE); return (kk); } /* steal_cancel */ /* Abstract: */ /* This routine gains control at driver cancel I/O calls. Note we don't bother with cancel_selective here (no disk driver uses it) but must handle driver cancel I/O. Driver cancel is present to allow I/O from a particular process and channel to be pulled out of queues. Unfortunately we alter irp$l_pid here, so the system routine can't in general find IRPs for the desired process. It would be desirable here to overcome this, since we *do* know where the IRP information is. Cancel is called fairly often, however...at every file close for example...so any code added here must be fast. Duplicating a fairly involved stretch of Macro-32 from the syscancel.mar routine seems like a poor idea from a maintenance point of view, but may be needed (or another call site for it may be) since each intercept using aux structures may keep its data in disjoint places. I *really* wish irp$l_pid didn't get reused for a postprocessing hook; a separate field would be SOOOOO much cleaner ... */ /* We will thus just dispatch down, but will pass a fake PCB down with pcb$l_pid pointing to this intercept's site for MSCP cancels. */ /* Inputs: Driver cancel-io inputs: channel, IRP, PCB, UCB, cancel reason Outputs: Cancel request is sent on to the appropriate underlying driver (whichever is the currently in use one). If the cancel is from the MSCP server, we duplicate the MSCP server's trick of supplying a fake PCB so that the IRP$L_PID value in the IRPs matches PCB$L_PID of the supplied fake PCB. We do not do this trick for other reasons (any more than the MSCP server does!). */ int steal_cancel(int chan, IRP* irp, PCB* pcb, UCB* ucb, int reason){ SW_UCB * myswucb; PTHPTR * mypath; int npath; int savipl; UCB* myucb; int (*myentry)(); long kk; /* short term temps */ PCB* fakepcb; fakepcb = pcb; myswucb = getswucb((UCB*)ucb); npath = myswucb->ucb$l_indrct; mypath = &myswucb->ucb$a_hstdta[npath]; kk = SS$_NORMAL; /* If the cancel is from MSCP and the reason for cancel is CAN$C_MSCPSERVER, we want to make this look like the PCB PID matches the packets. Use the same filthy dirty trick that MSCP does and fake the PCB! */ if (reason == CAN$C_MSCPSERVER){ fakepcb = &myswucb->ucb$l_fakepcb; } if ((long)mypath->ucb$l_cancl < 0){ (long)myentry = (long)mypath->ucb$l_cancl; /* irp->irp$l_ucb=myucb=(UCB*)mypath->ucb$l_hstucb; */ myucb=(UCB*)mypath->ucb$l_hstucb; kk = myentry(chan,irp,fakepcb,myucb,reason); } return(kk); } /* fdt_to_original - send the I/O to the original FDT routine, i.e., the one from whatever we intercepted. */ /* When called, UCB is our SW_UCB since this gets called for FDT code handled locally. Where this goes on, we need to send the code to the device we intercepted. */ /* Inputs: IRP from the user PCB for the user process SW_UCB for our intercept unit CCB for the channel used Outputs: Call to intercepted driver unit with the same arguments except for the UCB which is the intercepted master UCB. This routine is called only if the IO$_PHYSICAL code is given to the SW driver and is not modified as all such are expected to be. It will in general be needed only where the FDT routines are being modified. If one uses SWdriver as a model of how to intercept FDTs, this routine does what is needed to continue FDT processing after local FDT preprocessing has run, unless the I/O is in fact completed locally (i.e., in the intercept driver). */ int fdt_to_original(IRP* irp,PCB* pcb,SW_UCB* ucb,CCB* ccb){ long myfcn; UCB* icucb; FDT* oldfdt; int (* oldfcn)(); icucb = (UCB*)ucb->ucb$l_backlk; myfcn = irp->irp$v_fcode; oldfdt = (FDT*)ucb->ucb$l_oldfdt; (long)oldfcn = (long)(sizeof(long) * myfcn) + (long)FDT$PS_FUNC_RTN + (long)oldfdt; return (oldfcn(irp, pcb, icucb, ccb)); } /* driver cloned UCB entry */ /* Abstract: */ /* We use this scheme to make it easier to load units. The swdriver will therefore get loaded ALL the time, but only the template UCB will be set up initially. Then here we will fill in a cloned UCB, and also clear the deleteucb bit so the UCB will remain. That way code that wants to set up a switch driver unit need only assign a channel to get it created...no need to load the driver, connect a unit etc. etc. */ /* Inputs: cloneducb - The UCB just cloned by the system ddt - The DDT the system just created pcb - fork flag. If 1, call is from IPL 8 and we just call unit_init_fork directly. Otherwise we fork to call it. templateucb - Template (generally SWA0:) UCB Outputs: The template UCB is initialized and set up, either immediately on return (if pcb == 1) or after a fork has run. Synch is standard cloned-UCB synch. Note however that the deleteucb bit is CLEARED here so the created SW UCBs are not deleted when no channels to them exist. */ int sw_cloneducb(UCB* cloneducb, DDT* ddt, PCB* pcb, UCB* templateucb){ SW_UCB* myswucb; SW_UCB* tempswucb; PCB* fakepcb; cloneducb->ucb$v_deleteucb = 0; /* do NOT delete this new UCB */ cloneducb->ucb$v_template = 0; /* be sure this ucb is not marked as a template */ myswucb = (SW_UCB*)cloneducb; fakepcb = (PCB*)&myswucb->ucb$l_fakepcb; fakepcb->pcb$l_pid = (unsigned int)&fixsplit; cloneducb->ucb$l_fpc = &unit_init_fork;/* Point to fork routine address */ if ((long)pcb == 1){ /* If the PCB is 1, this call is from ipl 8 and may as well just call the unit_init_fork routine directly. */ unit_init_fork(0,(IDB*)fakepcb,myswucb); } else { exe_std$primitive_fork(ddt,pcb,(FKB *) cloneducb);/* Start fork process */ } return SS$_NORMAL; /* Return with success */ } /* sw-fakeformat */ /* */ /* Abstract: */ /* This routine assumes it is entered at IPL 8 holding forklock and is designed to do what io$_format processing would do, more or less, from IPL 8 threads of code in places like DUdriver that must set up switching when device UCBs are created. */ /* */ /* Input: */ /* buffer pointer to argument buffer */ /* bufsiz size of argument buffer */ /* ucb pointer to SW UCB */ /* Buffer formats: ufunc uswitch lo uswitch hi ulen rest 1 dvc name len dvc name bash 2 dvc name len dvc name unbash 3 unit to sw to idlfgs=0 switch, err if dvc bsy 3 " idlfgs=1 save next irp, busy dvc 3 " idlfgs=2 restart i/o, unbusying dvc, no switch 4 unit to rpt ? statblk len statblk data rpt stats Returns statistics on device paths this SW unit is using in an array. 5 UCB of device x x returns SW UCB, pathpointer array address, and some other info. Switch part of array gets the SW UCB or zero if none is found. This request can be sent to ANY SWdriver unit for any device to find out if the device is intercepted. It will return the SW ucb and info about the SWdriver unit that intercepted the device if it is intercepted. 6 IPID of server (both halves) mbx name len mbx name Sets the mailbox UCB and server IPID (if they are legal & found) into this SW driver unit UCB so it will communicate with the mailbox to talk to the server. 7 x x x x x Clears the SWdriver's references to the server. 8 Returns a magic number for this driver, adding 4 if the driver is online. Use from user mode code to ensure you are talking to swdriver. 9 As 1 and 2, but ulen is device UCB, not name. DDB and SB are 10 obtained from the UCB pointer and DDB pointer. This is done to simplify kernel mode setup. 11 x x x x x Returns SW UCB pointer, value of ucb$l_indrct, and the PTHPTR structure addresses of all PTHPTR structures, and of the one which is current. 12 Functions like 5, except that no input UCB need be supplied. It returns information about the devices the pointed-to SW device has associated with it. */ /* */ /* Output: */ /* */ /* Return value: */ /* SS$_FDT_COMPL shows that the routine completed correctly */ int sw_fakeformat(char* buffer, int bufsiz, SW_UCB *ucb) { /* Define the input buffer type as a union struct so each function can find a convenient type to use. */ #define MPSZ 128 #define MPSZQ 64 #define MPDN 512 /* Number of paths that will fit for statistics report */ #define MPNPTH ((MPSZ-5)/4) typedef struct inbuf { long ufunc; /* user function code (eg, bash/unbash) */ long uswitch; /* info passed in (eg add, which unit, encoded) */ long ulen; /* length of device name passed */ union { char dvcname[MPDN]; long moreprm[MPSZ]; } ; } INBUF; typedef struct qinbuf { long ufunc; /* user function code (eg, bash/unbash) */ long uswitch; /* info passed in (eg add, which unit, encoded) */ long ulen; /* length of device name passed */ union { char dvcname[MPDN]; uint64 moreprm[MPSZQ]; } ; } QINBUF; INBUF InBuf; int tstat; int tfcn; SB* mysb; INBUF * ibp; QINBUF * qibp; UCB* myucb; DDB* myddb; UCB* scratchucb; PTHPTR* mypthptr; SW_UCB* myswucb; /* allow us to pull a quad into 2 longs */ union { uint64 ttim; long ttiml[2]; }timu; long myswitch; int myswmod; int myunit; SW_UCB* scrswucb; int ufcode; int savipl; int curunit; /* currently used subunit, for where we are switching */ int kk,kkk,kkkk; /* temps */ PCB* trypcb; UCB* tstucb; DP_UCB* mydpucb; long* pcbptr; PTHPTR * newpath; struct dsc$descriptor_s mydvcnm; /* check the buffer is writeable */ if (bufsiz < sizeof(INBUF)){ return (SS$_BADPARAM); }; myswucb = ucb; ibp = (INBUF*) buffer; qibp = (QINBUF*)ibp; scrswucb = ucb; /* Set up the descriptor */ mydvcnm.dsc$w_length = strlen(ibp->dvcname); mydvcnm.dsc$b_class = DSC$K_CLASS_S; mydvcnm.dsc$a_pointer = &ibp->dvcname[0]; mydvcnm.dsc$b_dtype = DSC$K_DTYPE_T; myswitch = ibp->uswitch; /* extract word fields out of "myswitch" */ myunit = myswitch & 65535; myswmod = (myswitch >> 16); ufcode = ibp->ufunc; scratchucb = (UCB*) ucb; switch (ufcode) { /* 1 bashes disk, 2 unbashes */ /* 3 returns swdriver address and pthptr address if bashed by it. */ /* In these cases the "myswitch" arg low word is the unit to set or clear */ /* Note: don't really use functions 1 and 2 here except in tests if even then. Use 9 and 10 instead. */ case 1:{ mydvcnm.dsc$a_pointer = & ibp->dvcname[0]; mydvcnm.dsc$w_length = ibp->ulen; #ifdef DEBUG /* ini$brk(); */ #endif tstat = ioc_std$searchdev(&mydvcnm,&myucb,&myddb,&mysb); if ($VMS_STATUS_SUCCESS(tstat)){ /* in this case, "myswitch" is the index (counting from zero) of the path within this tuple for the switch driver. */ if (myswmod > 0){ /* if switch modifier exists, use "2p" alias here. */ mydpucb = (DP_UCB*)myucb; if ((myucb->ucb$l_devchar2 & DEV$M_2P) != 0){ /* If we are supposed to use the alternate UCBs, set that linkage up */ myddb = (DDB*)mydpucb->ucb$l_2p_ddb; myucb = mydpucb->ucb$l_2p_altucb; } } if(myucb->ucb$v_online){ mung(scrswucb,myucb,myddb,mysb,myswitch); } else { return (SS$_DEVOFFLINE); } } else { /* can't find device. Err...*/ return (SS$_DEVOFFLINE); } return(SS$_NORMAL); } case 2:{ mydvcnm.dsc$a_pointer = & ibp->dvcname[0]; mydvcnm.dsc$w_length = ibp->ulen; tstat = ioc_std$searchdev(&mydvcnm,&myucb,&myddb,&mysb); if ($VMS_STATUS_SUCCESS(tstat)){ if (myswmod > 0){ /* if switch modifier exists, use "2p" alias here. */ mydpucb = (DP_UCB*)myucb; if ((myucb->ucb$l_devchar2 & DEV$M_2P) != 0){ /* If we are supposed to use the alternate UCBs, set that linkage up */ myddb = (DDB*)mydpucb->ucb$l_2p_ddb; myucb = mydpucb->ucb$l_2p_altucb; } } unmung(scrswucb,myucb,myddb,mysb,myswitch); }; return(SS$_NORMAL); break; } /* more options here */ /* Rather than use a different code, we will use this area to do manual switching also. The buffer format will in this case use the myswitch variable as two words also. The format will be Word 0: unit to switch to (0 to NUM_HOSTS) Word 1: idle-device flags: If 0, just return an error if the device was busy (active I/O count non zero) and return success only if the unit was idle and successfully switched. If 1, flag startio intercept to save the next IRP address and leave the device busy until we later unbusy it. The unbusy call will be able to switch or not. If 2, restart I/O with no switch using the saved IRP if any. If there is none, just return. Note that the busying will not alter the outstanding I/O count ucb$l_outstnd since it is purely inside the switching driver here. The caller can block or unblock I/O but will need to wait for I/O to drain to switch paths; this is easier in user mode so we'll leave it for that. We'll restart I/O by forking and at fork level running REQCOM. */ case 3:{ /* in this case, "myswitch" is the index (counting from zero) of the path within this tuple for the switch driver. */ /* Again unit 65535 means "the next enabled unit" */ if (myunit > NUM_HOSTS){ for (kkk = 1; kkk <= NUM_HOSTS; kkk++){ /* get index of the next path (but go far enough to come back if we must) */ kkkk = (kkk + ucb->ucb$l_indrct)%NUM_HOSTS; newpath = &ucb->ucb$a_hstdta[kkkk]; if (newpath->ucb$l_enapth){ myunit = kkkk; break; } } } if (myunit > NUM_HOSTS){ /* if still above maxunits, error return */ return(SS$_ABORT); } newpath = &ucb->ucb$a_hstdta[myunit]; switch (myswmod){ case 0:{ if (ucb->ucb$l_outstnd != 0){ return(SS$_IVSTSFLG); } else { /* OK, looks like we should switch the unit. Do so. */ /* (MANUAL switch here !! ) */ device_lock(scratchucb->ucb$l_dlck,1,&savipl); /* *** fix this *** */ ucb->ucb$l_indrct = myunit; ucb->ucb$l_dwell = exe$gl_abstim; ucb->ucb$l_retries = 0; device_unlock((struct _spl*)scratchucb->ucb$l_dlck,savipl,1);/* *** fix this *** */ return(SS$_NORMAL); } } case 1:{ /* dummy this case here...cannot do it without i/o */ return (SS$_UNSUPPORTED); /* exit */ } case 2:{ /* Here we finish up the IRP that was queued before. */ /* dummy this case here...cannot do it without i/o */ return(SS$_IVSTSFLG); } default:{ return(SS$_BADPARAM); } } return(SS$_NORMAL); break; } case 4:{ /* Use subfunction 4 for reporting statistics, which include I/O counts and error counts for all subpaths, and things like time of last switch and anything else that looks worth having. We'll just dump the numbers into the user's buffer which we know to be writable already. */ ibp->moreprm[0] = NUM_HOSTS; /* Tell how long tables are */ ibp->moreprm[1] = sizeof(PTHPTR); /* Tell how long path tbl entry is*/ ibp->moreprm[2] = myswucb->ucb$l_indrct; /* return which is current path */ qibp->moreprm[2] = myswucb->ucb$q_swtime; kkk = 6; for (kk = 0; kk < NUM_HOSTS; kk++){ mypthptr = &ucb->ucb$a_hstdta[kk]; if (kk < MPNPTH){ /* Store statistics data for output. */ /* The data is I/O count, error count, and time of last I/O for each path */ ibp->moreprm[kkk++] = mypthptr->ucb$l_pthios; /* I/O count this path */ ibp->moreprm[kkk++] = mypthptr->ucb$l_ptherr; /* error cnt */ timu.ttim = mypthptr->ucb$q_pthtim; /* get time */ ibp->moreprm[kkk++] = timu.ttiml[0]; /* i/o time */ ibp->moreprm[kkk++] = timu.ttiml[1]; /* i/o time */ } } return(SS$_NORMAL); break; } case 5:{ /* Use subfunction 5 to get back information about bashes. In this case we expect we are being called by some external code which needs to find out if the device being passed is in fact having its path intercepted by swdriver. Rather than make other code need to know the details of our internal structures, use this code to export the knowledge. On input look for a device UCB address, and if it is intercepted by a SW UCB return the SW unit number and the pthptr array address. */ if (myswitch < 0){ /* UCB address looks potentially valid */ myswucb = getswucb((UCB*) myswitch); ibp->ulen = (long)myswucb; /* Return the SW ucb unit we found or zero */ scratchucb=(UCB*)myswucb; if ((long)scratchucb < 0){ ibp->moreprm[0] = scratchucb->ucb$w_unit; /* return SW unit no. */ ibp->moreprm[1] = &myswucb->ucb$a_hstdta[0]; /* Rtn addr of path pointer array also. */ ibp->moreprm[2] = myswucb->ucb$l_indrct; /* return which is current path */ ibp->moreprm[3] = &myswucb->ucb$l_pthord[0]; /* path sel order addr */ ibp->moreprm[4] = NUM_HOSTS; /* Tell how long tables are */ ibp->moreprm[5] = sizeof(PTHPTR); /* Tell how long path tbl entry is*/ ibp->moreprm[6] = (long)myswucb->ucb$l_vict; /* intercepted ucb */ ibp->moreprm[7] = myswucb->ucb$l_outstnd; /* Current I/O count */ } } else { /* error to fdt */ } /* complete this I/O */ return(SS$_NORMAL); break; } case 6:{ /* Use subfunction 6 to insert the server mailbox UCB and PID */ /* Input in device name area is MB device name stuff so we look it up (to reduce kernel code elsewhere) and PID in the other long. */ mydvcnm.dsc$a_pointer = & ibp->dvcname[0]; mydvcnm.dsc$w_length = ibp->ulen; tstat = ioc_std$searchdev(&mydvcnm,&myucb,&myddb,&mysb); if ($VMS_STATUS_SUCCESS(tstat) && (myswitch > 0)){ /* in this case, "myswitch" is the index (counting from zero) of the path within this tuple for the switch driver. */ ibp->uswitch = myucb; /* return mbx ucb */ ucb->ucb$l_mbxucb = myucb; /* Let's be sure the IPID here is a process IPID that really exists*/ pcbptr = (long*) SCH$GL_PCBVEC; if ((long)pcbptr >= 0)return; kkk = 0; for (kk=0; kk < SCH$GL_MAXPIX; kk++){ trypcb = *(PCB**)pcbptr++; if (trypcb->pcb$l_pid == myswucb->ucb$l_daemon ){ kkk = 1; break; } } if (kkk == 1){ ucb->ucb$l_daemon = myswitch; /* IPID of the daemon */ } } else { /* can't find device. Err...*/ /* Must place error in the fdt context area */ } return(SS$_NORMAL); /* complete this I/O */ break; } case 7:{ /* Function 7 just deletes the reference to the server */ ucb->ucb$l_daemon = 0; ucb->ucb$l_mbxucb = 0; return(SS$_NORMAL); break; } case 8:{ /* Function 8 sanity checks that this IS an SW driver by returning a magic number, octal 14747 or 6631 decimal. */ /* This is the pdp11 opcode for "mov -(pc),-(pc)" which instruction copies itself backwards in memory until it wraps around 32 kw. It is weird but odd, so will be taken for a kind of success. */ kkk = 6631; /* Add 4 if the SW unit is offline */ /* This should generally be the case for first-time access */ kkk = kkk + 4; return(kkk); break; } case 9:{ #ifdef DEBUG /* ini$brk();*/ #endif if ((ibp->ulen) < 0){ myucb = (UCB*)ibp->ulen; myddb = myucb->ucb$l_ddb; mysb = (struct _sb *)myddb->ddb$l_sb; /* in this case, "myswitch" is the index (counting from zero) of the path within this tuple for the switch driver. */ if (myswmod > 0){ /* if switch modifier exists, use "2p" alias here. */ mydpucb = (DP_UCB*)myucb; if ((myucb->ucb$l_devchar2 & DEV$M_2P) != 0){ /* If we are supposed to use the alternate UCBs, set that linkage up */ myddb = (DDB*)mydpucb->ucb$l_2p_ddb; myucb = mydpucb->ucb$l_2p_altucb; } } if(myucb->ucb$v_online){ mung(scrswucb,myucb,myddb,mysb,myswitch); } else { return (SS$_DEVOFFLINE); } } else { /* can't find device. Err...*/ return (SS$_DEVOFFLINE); } return(SS$_NORMAL); break; } case 10:{ if ((ibp->ulen) < 0){ if (myswmod > 0){ myucb = (UCB*)ibp->ulen; myddb = myucb->ucb$l_ddb; mysb = (struct _sb *)myddb->ddb$l_sb; /* if switch modifier exists, use "2p" alias here. */ mydpucb = (DP_UCB*)myucb; if ((myucb->ucb$l_devchar2 & DEV$M_2P) != 0){ /* If we are supposed to use the alternate UCBs, set that linkage up */ myddb = (DDB*)mydpucb->ucb$l_2p_ddb; myucb = mydpucb->ucb$l_2p_altucb; } } unmung(scrswucb,myucb,myddb,mysb,myswitch); }; return(SS$_NORMAL); break; } case 11:{ /* Return the path block for this SW unit */ if ((ibp->ulen) > 0){ /* fill in outputs here */ ibp->ulen = (long)myswucb; /* Return the SW ucb unit we found or zero */ scratchucb=(UCB*)myswucb; if ((long)scratchucb < 0){ ibp->moreprm[0] = myswucb->ucb$l_indrct; /* return path in use */ ibp->moreprm[1] = &myswucb->ucb$a_hstdta[0]; /* Rtn addr of path pointer */ ibp->moreprm[2] = &myswucb->ucb$a_hstdta[myswucb->ucb$l_indrct]; /* Rtn addr of path pointer */ } }; return(SS$_NORMAL); break; } case 12:{ /* Use subfunction 5 to get back information about bashes. In this case report on this SW device */ if ((long)ucb < 0){ /* UCB address looks potentially valid */ myswucb = ucb; ibp->ulen = (long)myswucb; /* Return the SW ucb unit we found or zero */ scratchucb=(UCB*)myswucb; if ((long)scratchucb < 0){ ibp->moreprm[0] = scratchucb->ucb$w_unit; /* return SW unit no. */ ibp->moreprm[1] = &myswucb->ucb$a_hstdta[0]; /* Rtn addr of path pointer array also. */ ibp->moreprm[2] = myswucb->ucb$l_indrct; /* return which is current path */ ibp->moreprm[3] = &myswucb->ucb$l_pthord[0]; /* path sel order addr */ ibp->moreprm[4] = NUM_HOSTS; /* Tell how long tables are */ ibp->moreprm[5] = sizeof(PTHPTR); /* Tell how long path tbl entry is*/ ibp->moreprm[6] = (long)myswucb->ucb$l_vict; /* intercepted ucb */ ibp->moreprm[7] = myswucb->ucb$l_outstnd; /* Current I/O count */ } } else { /* error to fdt */ return(SS$_BADPARAM); } /* complete this I/O */ return(SS$_NORMAL); break; } /* more options here */ /* Rather than use a different code, we will use this area to do manual switching also. The buffer format will in this case use the myswitch variable as two words also. The format will be Word 0: unit to switch to (0 to NUM_HOSTS) Word 1: idle-device flags: If 0, just return an error if the device was busy (active I/O count non zero) and return success only if the unit was idle and successfully switched. If 1, flag startio intercept to save the next IRP address and leave the device busy until we later unbusy it. The unbusy call will be able to switch or not. If 2, restart I/O with no switch using the saved IRP if any. If there is none, just return. Note that the busying will not alter the outstanding I/O count ucb$l_outstnd since it is purely inside the switching driver here. The caller can block or unblock I/O but will need to wait for I/O to drain to switch paths; this is easier in user mode so we'll leave it for that. We'll restart I/O by forking and at fork level running REQCOM. */ default: {return (SS$_NORMAL); break;} } return (SS$_NORMAL); }