LCLS Controls
SLC-Aware IOC Design
This
is a work-in-progress. Debbie Rogind 11/22/04
Updated
11/23/04 DBS Utilities, dbHdlrThread
Last
Updated: 12/06/04 Dictionary and DBS Utilites sections
Last
Updated: 12/08/04 dbHdlr
________________________________________________________________________
Database Service (DBS) Design
Quick Links
dbRecvThread – Detailed Design
dbSendThread – Detailed Design
dbHdlrThread – Detailed Design
The LCLS IOC contains two parts:
This document addresses the SLC IOC that communicates with the SLC Control System.
Communication with the SLC Control System passes either through the Database Service or through the SLC Message Service. The SLC IOCs must mimic the iRMXmicros in control and timing, to minimize changes in the SLC Control System. This document discusses the design of the Linac Coherent Light Source (LCLS) Stanford Linear Collider (SLC) Input/Output Controller (IOC) Database Service (DBS). Specifically, the Database Service design, Database Input/Output Utilities, and Console commands are discussed. The database service implementation described herein could be incorporated into any SLAC subsystem that communicates with the SLC Control System and utilizes the EPICS toolkit for device control.
The SLC IOC emulates many aspects of the SLC iRMX micro (micro), including maintaining its part of the VMS SLC database. Note that “database” is used to describe a “flat file architecture” throughout this document, and does not refer to a commercial or other relational database. Like a SLC micro, the SLC IOC DBS receives a piece of the entire SLC operational database at initialization time whose functionality is configured for it by the dbgen/dbinstall utilites running on the Alpha. Data is transmitted in a series of byte streams, called supertype (ST) blocks, or superblocks, over a TCP/IP LAN from the DBEX process running on the Alpha.
The data sent to the SLC IOC (and micro) is divided into four sections, physically in memory, according to its function and read/write attributes. Supertype 0 consists of pointer information (actually 4 byte offsets into the other ST 1-3 data blocks) by which each primary/unit/secondary name gets translated into memory address. (Primary/unit/secondary names are part of a naming convention which identifies a particular device.) Supertype 0 information gets written on the Alpha only at dbgen/dbinstall time. The other three Supertypes (1-3) of concern to the SLC IOC contain actual data. Supertype 1 is assumed to be stable, only changeable by the Alpha, and consists of configuration values. Supertype 2 data consists of setpoint values; any update of a Supertype 2 item in the control system originates only in VMS and causes both an update in memory in VMS and a message to the micro/ SLC IOC to do the same update in the micro/ SLC IOC copy of same memory. For the SLC IOC (as opposed to a micro), the EPICS database can also update Supertype 2 data, causing the SLC IOC to update both its local copy in its SLC database as well as send the Supertype 2 data to the Alpha. Any update of a Supertype 3 item originates in the micro/SLC IOC and causes an update in the micro's memory. A separate call sends accumulated updates to the Alpha to be stored in the corresponding VMS memory by DBEX. Any update of a supertype 4 item originates only in VMS and is never seen by the micro/SLC IOC (i.e., any micro/SLC IOC’s supertype 4 data resides only in VMS memory for use by application code in VMS).
Those familiar with the LINAC and PEPII iRMX micro computer code will recognize that most all “dbmain” functionality has been ported to the SLC IOC DBS to run on top of an EPICS OSI layer. The dbmain functionality has been split between dbRecv, dbSend, and dbHdlr threads, responsible for receiving, sending and handling data, respectively, from/to the Alpha.
The Database Service provides most, if not all, of the functionality contained in the iRMX micro’s REF_:[RMX.DBS] DBMAIN.C module.
· Amount of change to VMS should be minimal.
· Use existing VMS include files wherever possible; refer to VMS directory REF_:[RMX.INCLUDE] *
· Port existing iRMX application code (c, fortran, assembly) wherever applicable.
· Goal – generate same amount of network traffic as SLC micros
· More memory is available for use on SLC IOC – use to advantage for database storage to make database access quicker, if possible.
· Higher performance CPUs are being used, compared with micros – use to advantage
· Use EPICS libcom OSI libraries and platform independent libCom facilities to reduce coding time and to benefit from collaboration knowledge
· Account for different platform byte orientation. VMS packs; solaris and vxWorks (and many 32 bit architectures) byte align on 4-byte boundaries.
o The Supertype byte stream header (supertypehdr_ts), shared between VMS and all micros/SLC IOCS, is problematic, as it has three 2-byte words followed by four 4-byte words. Supertype header sent to/from VMS contains no padding.
· Messages from VMS control system are little-endian, VMS format, and packed
· Support ASCII representations for primary, secondary names and units
· SLC IOC needs supplementary data definition:
o primary ASCII to #
o secondary (secn) ASCII to # mappings for given primary
o datum width (for I or Z)
References for various areas are listed below.
DBEX:
(References for handling data traffic between the SLC IOC and DBEX process executing on the Alpha control system):
· Use VMS procedures in REF_:[RMX.DBS] DBMAIN.C and REF_:[RMX.SLCLIB] exnet.c as the design basis for interface to the DBEX Server process on the Alpha.
·
Use VMS assembly code REF_:[RMX.DBS] DBLISTU.A38
as the design basis for dblist and dbunits for accessing SLC supertype 0
database (used in dbIPL.c)
· Also, talk to the experts – Tony Gromme, Ken Underwood, Nancy Spencer, Ed Miller, Ron McKenzie, Ron Chestnut, Stephanie Allison, Terri Lahey.
EPICS references :
· IOC Application Developer's Guide, Release 3.14.6
· Where $LCLS = /afs/slac.stanford.edu/g/lcls/vol1/epics/site/src/slc, use $LCLS/msg/dbSend.c as a design reference for a queued thread and $LCLS/msg/msgRecv.c as design reference for a socket reading thread.
Also see SLC Executive and Message Service Design Presentations
SLC IOC Message Service Design
SLC IOC Utilities
Please refer to: SLC Database Service (DBS) Functional Requirements , by Stephanie Allison, for the complete list of Database Service requirements.
The primary function of the DBS is to keep its portion of the SLC database - shared data between the Alpha Control System and the SLC IOC – up to date. It accomplishes this by communicating to the DBEX process on the Alpha Control System. All of the database message communications between the SLC IOC and Alpha Control System is handled by the dbSend and dbRecv threads on the SLC IOC, and the DBEX process running on prod or dev Alpha. The SLC database transactions between VMS and SLC IOC occur through a TCP proxy.
There are two TCP/IP sockets per SLC IOC that handle DBS communications (“dbex” and “ctl”). These sockets are connections to a proxy, which also maintains two socket connections with DBEX. The proxy forwards data from DBEX to the proper SLC IOC after interpreting a proxy message “forward” header. Thus, the proxy allows DBEX to make two single socket connections in order to communicate with many micros/SLC IOCs.
The dbex socket is bi-directional and handles initial database downloads, updated data traveling in both directions, diagnostic messages, acknowledgements, and dbex up/down messages. The ctl socket is uni-directional and exists solely for DBEX to acknowledge back to the SLC IOC/micro that it received an update to the database.
DB Service consists of the dbSend thread, dbHdlr thread, dbRecv thread, database Input/Output utilities, database Console Commands, and DBS Utilities. The DB Input/Output utilities are global to all threads (refer to Specification for Database Input/Output Routines), while the DBS Utilities are used only by the db* threads and generally contain conversion utilities.
The Device Service function is shown in the DBS Block Diagram
The functionality contained in file REF_:[RMX.DBS] DBMAIN.C has been split into dbRecv and dbSend threads, while dbHdlr thread handles new data manipulations for the SLC IOC. The dbSend and dbHdlr threads are “queued message” threads, primarily taking direction from messages sent to their respective message queues. The dbRecv thread is considered a “socket” thread, as it primarily takes direction from incoming network messages from DBEX on the dbex socket.
dbRecv’s prime responsibility is to wait at the dbex socket for network messages from DBEX. It responds to incoming data messages by storing data, in VMS format, as well as by sending queued messages to dbSend to acknowledge DBEX back (when DBEX requests, via a flag in the supertype header) on the dbex socket. Note that currently, acknowledges back to DBEX are only requested by DBEX during IPL, and not during ST 2 data updates. After the SLC IOC receives a database download at startup, it sends a message to dbHdlr to convert the data.
dbSend takes direction from queued messages sent from the slcExec, dbRecv, and “device service” threads. As part of carrying out its functions, it sends all data to DBEX via the dbex socket. For outgoing network messages consisting of supertype 2 or 3 updated data, it requests DBEX to acknowledge back and waits for that acknowledgement on the (dedicated) CTL socket immediately after sending the message.
dbHdlr’s prime responsibility is to offload (large amounts of) data handling from dbRecv or dbSend. It initializes DBS globals and waits at its queue for the message from dbRecv to convert data (compressed ST0 data to string-based format). Upon DBS restart or exit, it resets globals and releases resources. It is desired to be able to stop/restart the DBS and redownload the database while the EPICS application code, co-existing in the SLC IOC, continues to run. dbHdlr coordinates these efforts.
The following sections describe the ways in which to access the SLC IOC DBS.
The SLC IOC communicates with DBEX, the Database Executive process that runs on the SLC Control System under VMS on the Alpha. It uses a byte stream composed of a proxy header (intended for proxy use) followed by data service header (or supertype header, interpreted by DBS) followed by data (for data-carrying messages). These messages are tcp/ip, and are managed by a proxy host. The proxy host establishes TCP/IP connections with DBEX in order to communicate with many micro/SLC IOCS using their socket connections. The two DBS socket connections on each SLC IOC are:
Both dbRecv and dbSend threads are responsible for the bi-directional communications on the dbex socket, while dbSend communicates with the uni-directional Ctl socket.
When any job thread or other utility decides to update the Alpha with a job’s updated data, it calls the reentrant utility, “dbupdate(job)”, which sends the “DB_UPDATE” message to dbSend message queue.
dbRecv also sends messages to dbSend Queue in order to have it send DBEX acknowledgements for supertype data, as well as diagnostic requests.
General database input/output utilities direct the reading and writing of data from / to the slc database and application code (other job threads). See Database I/O Utilities.
Console commands entered at the EPICS iocsh, RTEMS Cexp shell, or vxWorks shell, access the database (eventually via the DBS I/O utilities above) for the user.
See Console Commands API.
For the SLC IOC, it is desired to be able to stop and restart the SLC-only threads while the EPICS applications continue running. Furthermore, it is desired to redownload the SLC database upon command, after which device threads reinitialize and clear their dblists. In order to prevent heap memory fragmentation, most data structures which are allocated by DBS will be under control of a memory pool manager, namely the EPICS freeListLib.
The byte stream messaging between DBEX and the SLC IOC always contains a fwd_hdr_ts proxy header which the proxy interprets, and a dbsuptypehdr_ts supertype header, which the Alpha DBEX process and SLC IOC db* threads use to communicate back and forth. (The supertype header is equivalent to the Message Service message header.)
Name |
Data type |
Represents |
fwd_hdr_ts |
|
|
ip_port_u len user cmd crc |
ip_port_tu int4u user_field_ts int1u int1u |
Lower half of the ip address, and the port number Message bytecount - this fwdheader User defined; chunk count for large buffers; Fwd_server command, e.g. 8 bit crc over header; currently set to 0x55 |
dbsuptypehdr_ts |
or |
dbsuptype_tu = union |
network netlsn id <<<<< Note len micr blkbeg blkend
|
int2u int2u int2u potential byte int4u Int4u int4u int4u |
Type of data alignment
mistmatch here
>>>>> Size of superblock Microname Copy to location in superblk Copy from location in superblk |
datw[ ] |
Array of int2u |
type-specific data; packed; max size is NETBUFLEN minus sizeof(dbsuptypehdr_ts)/2 |
The format of the data (datw[]) following the supertype header is dependant upon flags in the supertype header. The dbsuptype_tu definition makes a union of the supertype header (dbsuptypehdr_ts) and the data, such that any element of the supertype header or the data following can be accessed as a 16-bit data word. (This is important when unpacking the packed VMS formatted supertype data into native, structured format.)
The legacy VMS ST 0 byte stream, comprised of pointers to primary/unit/secondary data, contains a hash table followed by a series (X number) of unit/primary/secondary (UPS) blocks. Each UPS block is followed by its associated nsub number of secondary blocks (secn). In order to save memory on the legacy micros, numbers were associated with each primary, unit, and secondary name in lieu of storing the 4-char ASCII representations for each. In the SLC IOC, ASCII names will be stored instead of numbers. The ASCII representation facilitates programmers and is necessary for console users when typing in primary/unit/secondary names. The ASCII name representations, along with its associated secondary data information (from ST0 block and file on NFS) is translated into a “dictionary” for the SLC IOC (refer to Dictionary).
fwd_hdr_ts proxy header |
Dbsuptypehdr_ts supertype header |
hash length array listhead ptrs array node counts |
UPS block 0 secn block [0 – nsub]:
supn | subn
fmt | slen
sptr = offset in supn … |
…. |
UPS block X secn block [0 – nsub] |
Where a UPS block contains:
3 links – next UPS, unit, collided nodes len - ups + nsub’s catn - prim # unit - unit # nsub - # of secns |
Supertype 1-3 byte stream data consists of actual data values (pointed to by ST 0).
fwd_hdr_ts proxy header |
Dbsuptypehdr_ts supertype header |
Data |
Data |
data
|
Data |
Data |
data …. |
The dbRecv receive buffer and the dbSend send buffer, both allocated in their respective threads, have similar structure to the overall byte streams. They maintain both headers and contain data, all of which are in VMS format. Upon receiving the data stream into the receive buffer, dbRecv converts the incoming dbsuptypehdr_ts into native format so that it can check the contents of the message. The native format is stored in the thread’s queued message variable, the structure of which is shown in the next section.
Name |
Data type |
Represents |
Dbsndrcvbuf_ts |
|
Receive and send buffers |
proxy_hdr supblk |
Fwd_hdr_ts dbsuptype_tu |
Proxy header Superblock header + data |
The dbmsgmail_ts contains all information required when communicating from db thread to db thread. The msgheader_ts contains the message ID (function). The nativeSB contains the native supertypehdr_ts, converted in dbRecv. The diagnostic data field is to service the diagnostic message received by dbRecv from DBEX; the reply is formatted and sent to DBEX from dbSend.
Name |
Data type |
Represents |
Dbmsgmail_ts |
|
Q messages |
Msgheader nativeSB dbdiag |
msgheader_ts dbsuptypehdr_ts dbdiagmsg_ts |
Msg header Native superblk hdr Diagnostic data |
The dictionary is a string based hash table whose entries contain exemplary information (meta data) about each primary/unit/secondary (PUS) data, namely:
The PRIMARY.MAP ASCII file is created as part of the dbGen/dbInstall process on the Alpha. It is then released to a TBD production area on NFS in order for the SLC IOC to read it. On each line it contains primary name, primary number (catn), secondary name, secondary number (secn), format, width of data (bytes), and number of data items. Note that for each primary, secondary numbers are relative to their associated primary. A secondary of one primary may have a different number than the same secondary name of another primary.
The PRIMARY.MAP file is created so that the SLC IOC can a) convert primary and secondary numbers into their ASCII name representation, and b) have access to the data width information for each secondary. Primary names that do not exist in the SLC IOC database are still entered in the dictionary for name validation.
The native dictionary structure is created from a) the converted number-oriented, compressed, VMS formatted superblock 0, and b) by reading the PRIMARY.MAP file, available on an nfs mount point at location pointed to by environment variable.
PRIMARY.MAP format:
Primary catn
Secondary secn format
width number
QUAD 1 BMON 135 R 4 1
QUAD 1 KTIM 31 T 8 1 …
NOTE: file lines containing any given primary name must be contiguous in the file; the algorithm used by dbST0CreateDict() depends on this contiguousness.
For hash facilities, the dictionary uses EPICS libCom gphash
(general purpose hash) as a base for its own dbhash facility. gphash sets up an
internal hash table of ‘tablesize’ and returns a pointer to it. It also
maintains a mutex which is not needed during a) single-threaded write-only
initialization while other threads are locked out waiting for downloadEvent,
and b) read-only access during operations. (This mutex requires less than 2
usecs when the mutex is available, so it doesn’t warrant modifying the gphash
facility just for selectable mutex locking, unless we call it more than
100 times / second.)
However, gphash doesn’t use memory
management facilities, thus it was decided to copy gphash to dbhash and modify
it to use routines available in freeListLib.
Dbhash will use memory pools for the hash array (ELLIST[tablesize]),
and for each GPHENTRY. It will remove the mutex locking and it will substitute
the strcmp call with strncmp such that hash strings will not need to be
null-terminated.
When organizing the way data is accessed in the dictionary, there are four possible ways to arrange the data because there are four use cases that fall out of the various database input/output and shell routines. A primary (P) must always be specified, while the other two arguments, unit (U) and secondary (S), may be any combination of ALL*, or specified.
The four use cases are outlined in the bullets, and figure below.
To implement all four optimizations would be costly in memory and may be overkill. At a minimum, the P** case should be implemented, as it can handle all four use cases. Shell commands do not need to be optimized, as they occur at “human speed”, so PU* does not need to be implemented. The P*S case can be handled by P** (not quite as efficiently), and will be re-evaluated for implementation when we address how often the magnet and bpm jobs receive SCP requests with ALL* for units. For those threads that respond to a specific SCP request, they will be implementing the PUC use case during runtime. This means that the P** and PUC cases will be implemented, at a minimum. Every “primary” in PRIMARY.MAP will be entered into the hash table, regardless of its presence in this particular SLC IOC for primary name error checking. Each “primary secondary” doublet from PRIMARY.MAP could also be entered for ease of secondary name error checking; at the moment this is a TBD.
The following figure shows the minimum Dictionary implementation:
Each dbhash table entry is of type DBHENTRY. The DBHENTRY is defined as:
Name |
Data type |
Represents |
DBHENTRY |
|
Hash table entry |
node name userPvt
|
ELLNODE Const char * Void * |
Link node “primary unit secondary” Pointer to either: dbdictPUS_ts or dbdictP_ts |
‘name’ in the above structure maintains our “hashing string” and points to a dictNameTriplet_ts (for PUS name) or points to a dbdictNameSgl_ts (for a single P, U, or S name), while userPvt points to either each dbdictUPS_ts secondary meta data entry or each dbdictP_ts primary entry.
For PUS, the “hashing string” consists of the unique “primary unit secondary” string. Memory is allocated for the dbdictNameTriplet_ts and a const char * pointer is established with the following:
Name |
Data type |
Represents |
dictNameTriplet_ts |
|
Triplet PUS name string |
Name[12] |
Char |
Non- null-terminated “primary unit secondary” string name; length = 4-4-4 = 12 bytes |
Note that gphash uses “strcmp” which requires use of null termination for the name string. This will be changed to “strncmp” in dbhash, such that we have char[12].
For the hashing string used with primary, unit, or secondary name only, the following structure is used:
Name |
Data type |
Represents |
dbdictNameSingle_ts |
|
Single P-U- or S name string |
Name[4] |
Char |
Non null-terminated “primary” or “unit” or “secondary” string name; length = 4 bytes |
Each DBHENTRY entry that points to a dbdictNameTriplet_ts also has its userPvt pointer pointing to a dbdictPUS_ts structure that contains the secondary meta data. The “primary unit secondary” meta data has the following structure definition:
Name |
Data type |
Represents |
dbdictPUS_ts |
|
SLC Secondary data type |
supn count sptr width format |
short short unsigned long short char |
Supertype number Word count Word offset into supn block 1,2,4,8 bytes; data width (I,A,R,Z,S,T) |
Entries for unique “primary” name will be entered into the hash table of the dictionary. When adding these, a pointer to dbdictNameSgl_ts, containing the primary - only name string is entered in the DBHENTRY name field, and the userPvt pointer of DBHENTRY will point to a dbdictP_ts structure. If the primary is not present in the SLC IOC, the userPvt pointer of DBHENTRY is null.
The dbdictP_ts structure represents a linked list head of its respective units present from the ST0 data for that primary.
Each primary entry has the following structure:
Name |
Data type |
Represents |
dbdictP_ts |
|
Primary linked list head for units |
listUnits |
ELLLIST |
List head for linked units |
Note: listUnits must be first element in structure.
Each unit node in the units linked list is in monotonically increasing order by unit number, and is the linked list head for all secondaries contained in that primary/unit.
Each unit in the linked list has the following structure:
Name |
Data type |
Represents |
dbdictU_ts |
|
Unit linked list node for each primary |
node listSecns name
|
ELLNODE ELLLIST Const dbdictNameSgl_ts * |
Unit Linked list node List head for linked secondaries Unit (number) name |
Note: node must be first element of structure.
Each secondary node in the secondary linked list (which has a dictU_ts as its linked list head), has the following structure:
Name |
Data type |
Represents |
dbdictS_ts |
|
Secn linked list node for each unit |
node dictEntry name |
ELLNODE DBHENTRY * Const dictNameSingle_ts * |
Secondary linked list node Pointer to dict entry containing PUS Secondary name |
Note: node must be first element of structure.
Each secondary entry contains a pointer to the “primary unit secondary” DBHENTRY where the dbdictPUS_ts secondary meta information is pointed to.
The Dictionary figure is repeated below, with above structures labeled.
The epics libCom memory manager utilities in freeListLib will be used to pool memory for storage of the dictionary structures. The freeList library of routines allocates a large pool of memory, and manages this large pool of memory as many smaller blocks of memory all of the same size. Thus, there must exist a memory pool manager for each different structure type. Dbhash will be rewritten to manage the memory pool for the hash table array and the GPHENTRY structures. File dbdict.c, whose utilities are called from dbHdlr, will mange the other six data structures defined above in support of the dictionary.
The following is an analysis of
the different structures in support of the dictionary, as well as the estimated
byte count of each structure. Note that byte count has been adjusted to the
next largest 4 byte boundary to account for byte alignment on 4 byte
boundaries.
dbhash allocates the following different structures:
dbhInitPvt –
dbhAdd –
dbHdlr, who creates and manages the dictionary allocates the
following:
DBS Global data is shared between all DBS threads and initialized by dbHdlr thread. Even though the data is available in global space to all threads, only DBS threads or utilities use and manage this data.
Name |
Type |
Represents |
dbnodes_p[4] |
dbsuptype_tu |
Pointers to superblocks 0-3 dbRecv mallocs, writes pointers & data while other threads are blocked at db download event. After download event pointers read-only (ptr to ST0 & data deleted; rest never change) |
dict_p |
void * |
Pointer to dictionary; new char-based gphash table created for housing supertype 0 data (secondary data & location) and valid ASCII names. gphash has its own mutex protection for add/delete/find dictionary utilities. |
dbex_up dbVersion |
epicsBoolean char * |
Status of dbex up (T) or down (F) Db major / minor version w dbex_up msg |
dbRWMutex |
epicsMutexId |
Activiated for reads or writes from/to supertype 1-4 data. dbRecv (for Alpha VMS updates), and dbl* I/O utilities access |
dbhilo_updates [N_JOBS] |
Dbhilo_job_ts |
Each “job service” array contains pairs of offsets to upper and lower bounds of modified ST2/3 data. Acc’ed by utility rte dbhilo_update called from dblput. |
dbhiloMutex [N_JOBS] |
epicsMutexId |
Activiated for reads or writes from/to dbhilo_updates[ ]. Acced by dbSend, and private utility dbhilo_update called by dblput |
dbAckEvent [N_JOBS] |
epicsEventId |
Job service threads wait at this event until their updated data has been acknowledged by Alpha |
dbAckStatus [N_JOBS] |
vmsstat_t |
Job service threads receive this status after waiting at dbAckEvent until their updated data has been acknowledged by Alpha |
There are two other globals, dbExists and downloadEvent, which are created prior to DBS thread creation, and thereafter are partially maintained by DBS (and partially by slcExec). These globals, dbExists, and downloadEvent, are initialized to dbExists = F and downloadEvent semaphore to blocking such that any threads who want to access data after they are created and before the database is downloaded, are blocked. Similarly, any console users who want to access data before the database is downloaded have an error immediately returned from their console command after the underlying code checks the dbExists flag. The dbHdlr thread is responsible for setting the dbExists flag and resetting the downloadEvent (after download) during initialization.
<Note, we decided not to implement a dbstop or dbinit:
Upon a “dbstop”, dbHdlr resets the dbExists flag. Upon a “dbinit” and after a re-download, dbHdlr sets the dbExists flag and unblocks the downloadEvent.>
The dbStopEvent is set by slcExec when a user types in
‘dbstop’ at the console. A
”DB_STOP” message is also sent to dbHdlr. This causes the DBS to clear the database,
clear the dbhilo update lists, and to reset the dbExists flag. dbRecv throws away updates coming from the Alpha. If an IPL
occurs during this state, an error is logged. Updates going to the Alpha are
also thrown away by dbSend in this state. Diagnostic requests, as well as DXUP,
DXDN messages from the Alpha are processed normally. Dbl* utilities should not
be called, as job threads who access the db are blocked at the dbStopEvent.
Upon the user typing in ‘dbinit’ at the console, slcExec sets the downloadEvent to block, resets the dbStopEvent, and sends a “DB_DOWNLOAD” message to dbSend in order to initiate the IPL process. After download, dbHdlr sets the dbExists flag and sets the downloadEvent to unblock.
slcThreads[thread enum] is a global array of thread information. Each DBS thread must maintain the fields in its array.
Name |
Type |
Represents |
tid |
EpicsThreadId |
Thread identifier |
active |
EpicsBoolean |
True if thread is active / looping; set by thread |
stop |
EpicsBoolean |
True if thread should stop; set by slcExec |
mqId |
epicsMessageQueueId |
Message queue identifier |
During thread initialization, all of the DBS threads set :
· active = T
· mqId = message queue id from create call
slcExec sets tid = thread id from create call; it also sets stop = F
During operations, all threads must periodically check their stop flag, and if it is set T, they must exit gracefully. Exiting consists of
dbRecv maintains slcSockets[slcDbex] and dbSend maintains slcSockets[slcCtl].
The following describes, at a high level, the threads, data, and routines involved with downloading the database.
dbRecv sends a DB_CONVERT queued message to dbHdlr after it receives the last piece of supertype 3 data. It is up to the dbHdlr thread to implement all steps necessary to transfer the ST0 data into the dictionary format.
into dictPUS_ts structure
The following is PDL for creating dictionary entries based on file data and ST0 data.
For each unique primary name in PRIMARY.MAP file
Make “primary” entry in dictionary
call dbunitsST0 (ld_p, catn)
if (status OK and ld_p.len>0)
for each unit# in list
dbgetUpsST0(uptr, catn, unit)
link unit in “primary” (for ALL*)
for every secondary in file
dbgetSecST0(sptr,uptr,secn)
store “primary unit sec” with
combined ST0 & file data;
+ link sec in unit entry (for ALL*)
During normal operations, tasks on the Alpha may issue a dbput which cause an update of supertype 2 data to be sent to micros/SLC IOCs. The byte stream is similar to the supertype 2 data received during IPL. The id of the supertype header is masked for:
·
Receives
Supertype 2 (setpoint) update messages from DBEX
·
Note: Currently DBEX does not set the request ack
flag in the supertype id header which would cause dbRecv to send “DB_DBEX_ACK”
msg to dbSend.
·
Lock dbRWMutex
·
Copies super block recv’ed (dbmsgcopy) to SLC IOC
ST2 block
·
Unlock dbRWMutex
Updated data flowing from the SLC IOC to the Alpha is dicussed in this section.
Supertype 2 and 3 data is updated in the EPICS database by various ioc tasks. The SLC IOC has various asynch and handler jobs that read the data from the EPICS database and then update the slc database using dblput calls. Dblput reentrant utility stores the data to the slc database (locking and unlocking dbRWMutex around the memcpys) and then calls dbhilo_update utility in order to store the upper and lower bounds of the data offset (hi/lo offset pair). These hi/lo offset pairs are the actual offsets into the supertype 2 or supertype 3 data memory and are stored in a dbhilo_update array unique for each job and for each supertype (2, or 3). The dbhilo_mutex[job] is locked and unlocked around the memcpy when updating the dbhilo_update array.
When a job thread has determined that it wants to send its updated data to the Alpha, it calls the reentrant utility dbupdate(). Dbupdate() determines the calling job’s job id from slcThreads[thread enum] global and then sends the message “DB_UPDATE”, along with job id to dbSend queue. Dbupdate() then waits on the ackEvent[job],
For each update pair in dbhilo_update[job], do the following:
1) Successful Ack
2) Invalid msg seq #
3) Timeout
4) Socket error
When all updates for job have been sent, or there was an exit condition:
*Note that if it is too hard to configure the CTL socket with non-blocking reads, a new thread will be created, dbCtrl, which will synchronize with dbSend. dbSend will issue sleeps() of 1 second each and check the ctlEvent[job] flag each time, for up to timeout value of 15 seconds. It will also compare the msg seq # received by dbCtrl to determine whether it was a valid ack or not.
Also see: Also refer to DB Service Download (IPL)
And DB Service Receives Updates from Alpha
The dbRecv thread performs the following at initialization
· dbupdate sends DB_UPDATE(ALL*) to dbSend; doesn’t block on any ackEvent for ALL* case
Also see: Also refer to DB Service Download (IPL)
And DB Service Receives Updates from Alpha
When the slcThreads[msgRecv].stop flag == epicsTrue
The
dbRecv thread uses the following globals:
The dbRecv thread manages the slcSockets[sdDbex] Database Service socket. Whenever a failure is detected the dbRecv thread is responsible for re-connecting with the Proxy (dbSockConns()).
The epics libCom memory manager freeList will be used to pool memory for local storage of the supertype data received. This library of routines allocates a large pool of memory, and manages this large pool of memory as many smaller blocks of memory (all of the same size). The granularity of “smaller blocks” will most likely be the maximum DBS message size (from DBEX) of 8k bytes.
The
slcCmlog utilities are used for logging the following status and error
messages:
TBD
TBD
epicsShareFunc void epicsShareAPI dbRecvThread(void)
local vmsstat_t dbInitGlob(void);
local void cleanup(void);
local vmsstat_t dbSockConns(void);
local void dbSockClose (void);
local vmsstat_t dbRead (
dbsndrcvbuf_ts *inmsg_ps,
const unsigned short *max_bytecount_p,
int *actual_bytecount_p,
int *sbbytecnt);
local void dbmicromail(const dbsndrcvbuf_ts *mail2_p,
const unsigned long actrcvbc,
const int sbbytecnt);
local void dbmsgcopy(const dbsndrcvbuf_ts *mail2_p,
unsigned long supblkx,
unsigned long nwords);
local vmsstat_t dbsuperallo (const dbsndrcvbuf_ts *mail2_p,
unsigned long supblkx);
local void dbmdiag_sendrpy(dbsndrcvdiagmsg_ts *diagmsgbuf_p,
unsigned
short dgmsgbc);
local vmsstat_t formST2Ack (const unsigned short id);
Also refer to DB Service Download (IPL)
And DB Service Sends Updates (originating in EPICS) to Alpha
The dbSend thread performs the following as initialization
When the slcThreads[dbSend].stop flag == epicsTrue
The
dbSend thread uses the following globals:
epicsShareFunc void epicsShareAPI dbSendThread(void)
static void cleanup(void);
static void chkSockConnection(void);
static vmsstat_t dbdownloadme(void);
static vmsstat_t dbWriteDbs(int sd,
const unsigned short id,
dbsndrcvbuf_ts *outmsg_ps,
const unsigned short *max_bytecount_p,
unsigned short *actual_bytecount_p);
static vmsstat_t dbAck (fwd_hdr_ts *pFwd, dbsuptypehdr_ts *pSup);
static vmsstat_t dbDiagReply (fwd_hdr_ts *pFwd, dbsndrcvdiagdata_tu *pDiag);
The dbHdlr thread performs the following at initialization
When the slcThreads[dbHdlr].stop flag == epicsTrue, or any other detected “terminal” failure:
The following DBS global variables are initialized by dbHdlr:
dbnodes_p[4] |
dbsuptype_tu |
Pointers to superblocks 0-3 dbHdlr mallocs, writes pointers & data while other threads are blocked at db download event. After download event pointers read-only (ptr to ST0 & data deleted; rest never change) |
dict_p |
void * |
Pointer to dictionary |
dbex_up dbVersion |
epicsBoolean char * |
Status of dbex up (T) or down (F) Db major / minor version w DXUP messagesents on IPL and when DBEX comes up |
dbRWMutex |
epicsMutexId |
Activiated for reads or writes from/to supertype 1-4 data. dbRecv (for Alpha VMS updates), and dbl* I/O utilities access |
Dbhilo_updates [N_JOBS] |
Dbhilo_job_ts |
Each “job service” array contains pairs of offsets to upper and lower bounds of modified ST2/3 data. Acc’ed by utility rte dbhilo_update called from dblput. |
dbhiloMutex [N_JOBS] |
epicsMutexId |
Activiated for reads or writes from/to dbhilo_updates[ ]. Acced by dbSend, and private utility dbhilo_update called by dblput |
dbAckEvent [N_JOBS] |
epicsEventId |
Job service threads wait at this event until their updated data has been acknowledged by Alpha |
dbAckStatus [N_JOBS] |
vmsstat_t |
Job service threads receive this status after waiting at dbAckEvent until their updated data has been acknowledged by Alpha |
Refer to Dictionary memory management
The
slcCmlog utilities are used for logging the following status and error
messages:
TBD
TBD
epicsShareFunc void epicsShareAPI dbHdlrThread(void)
File dbIPL.c contains routines to transfer (or convert, but here convert does not include endian or byte alignment) the VMS ST0 structure to the dictionary structure. See DBS Utilities section below.
This section describes utilities that only the DB Service threads call.
dbUtil.c contains conversion and debugging utilities that print to the console.
File dbST0.c contains routines to transfer (or convert, but here convert does not include endian or byte alignment) the VMS ST0 structure to the dictionary structure. Since the routines operate on VMS formatted data from DBEX, some “legacy-style” data types are used (int2u, int4u, etc) for compatibility.
The utility :
called from dbHdlr
uses the following local static procedures to process the downloaded ST0 meta data, along with reading PRIMARY.MAP file:
const int2u bytes_in_element,
const int2u num_elements);
const int2u prim,
const int2u unit,
const int2u secn);
Above uses suptype0_ts as the definition of the ST0 structue:
typedef struct
{
int2u headerw[11]; /* packed supertype header */
int2u hshl; /* size of hash array */
int4u hTbl[DBHASH]; /* Hash table - array of offsets */
int2u hcTbl[DBHASH]; /* Counts of nodes in above array */
} suptype0_ts;
Above uses dbST0header_ts as the ST0-type of list populated by dbST0list() and dbST0units().
typedef struct
{
int4u max; /* max length of list (words; includes this hdr) */
int4u cur; /* current length of list */
} dbST0header_ts;
All ST0-type lists used in discovering which primaries and units are configured in ST0 are currently malloc’ed and free’ed. This design should be modified to use the new-style dictionary free lists.
dbdict.c contains utilities (mainly used by dbST0) to insert entries from ST0 into the dictionary. Please reference the Dictionary section for dictionary structure and data structures used. The dbdict.c routines use dbhash.c utilities for hash table management.
const dbST0header_ts * dbunits_ps,
const char * prim_p,
const char * secn_p,
const dbST0header_ts * dblist_ps,
const short supn, const short count,
const short width, const char fmt);
Utility dbdictInsSecnInAllUnits() uses local static dbdictInsPUS() procedure to insert a “prim secn unit” entry in hash table as it inserts a linked list of units belonging to a given prim:
const short supn, const short count,
const long sptr, const short width, const char fmt);
gphash* has been copied to dbhash and the following modifications have been made to dbhash.c (and dbhash.h):
Dblist.c contains utilities for managing the two free lists used by the DB I/O utilities – dblistSmFreeList and dblistBigFreeList. The dblist routines use epics utility freeListLib.c; refer to freeList.h for the prototypes.
When a small list runs out of room, it gets a big list, copies the small list contents to the big list, and frees the small list. Similarly, when a big list runs out of room it gets more space directly from the heap, copies its contents to the new memory, and frees the big list. If the dbheader_ts (refer to Database Input/Output Utilities) list size is other than DBL_SZBIG or DBL_SZSM, it is assumed the list came from the heap for later freeing purposes (logic in dbfree()).
dbShell contains the implementation (callbacks) for the Shell Commands.
The callbacks for the shell commands have the name “<shell command>Callback” . The callbacks will, in general, first check the dbExists flag. If the database exists, they call underlying routines which return vmsstat_t and pass the desired return data in the first argument (as a 2-byte integer). If the underlying routine had an error, the error is output, otherwise the callback routine converts desired value(s) to ASCII prior to output to the console.
Any support utility is contained in dbshellUtil.c. If it is desired to use these utilities from other threads, they can be moved.
Returns the native data type (R,I,Z,A,S,T) format as a single character
o dbhEntry_p = dbhFind(“P”)
o get first unit from unit linked list
o fmt = dbgetformat(P,U,S)
o if error, print
o output fmt
epicsBoolean dbverifyPUS(const char * const prim, const
char * const unit, const char * const secn, )
· dbhEntry_p = dbhFind(“PUS”)
o if dbhEntry_p != null, return true
o else dbhEntry_p = dbhFind(“P”)
· if dbhEntry_p->userPvt = null, then it’s a primary name error
o log error (console or cmlog)
o return false
· Else
o get first unit from unit linked list
o find match for secn – return true
o if no match, log error (console or cmlog)
§ return false
char * dbgetformat
(const char * const prim, const char * unit, const char * const secn )
Returns the single character format of PUS; returns ‘/0’ (null) if not found in dictionary.
o dbhFind(“PUS”)
o return dbhEntry_p ->userPvt->format
Returns the data type data width: 1,2,4 or 8 bytes.
o dbhEntry_p = dbhFind(“P”)
o get first unit from unit linked list
o width = dbgetwidth (P,U,S)
o scanf (width) and output ASCII
int dbgetwidth(const char * const prim, const
char * const unit, const char * const secn)
Returns the integer byte width of PUS; returns zero if not found in dictionary.
o dbhFind(“PUS”)
o get dbhEntry_p ->userPvt->width
o byte swap, return value
Returns the number of elements for the secondary.
o dbhEntry_p = dbhFind(“P”)
o get first unit from unit linked list
o count = dbgetcount (P,U,S)
o scanf (count) and output ASCII
int dbgetcount(const char * const prim, const
char * const unit, const char * const secn)
Returns the integer number of elements for the secondary; returns zero if not found in dictionary.
o dbhEntry_p = dbhFind(“PUS”)
o if null, then its an error
o get dbhEntry_p ->userPvt->count
o byte swap, return value
Returns the meta data for the secondary – count (number of elements), followed by format (R,I,Z,A,S,T), followed by width ie “23A4”
The callback routine calls
dbgetcount(PUS); convert to ASCII
dbgetformat(PUS), and
dbgetwidth(PUS); convert to ASCII
Then concatenates the three strings for output to console of the form <count><format><width>
const char *
const secn)
Dump the primary/unit/secondary value.
One of the following fields can be ALL* (not both): unit, secn.
TBD: have both unit and secn be ALL*
o dballoc(lp)
o dblist (lp, P, U, S)
o dblget (lp, ld)
o convert to ASCII and output
Dump all the units, if any, for given primary.
o dballoc(ld)
o dblunits(ld, P)
o for each unit in ld list, output
const char *
const secn,
char * const
value1, char * const value2, ...)
Converts ASCII values to proper format and puts them into the
SLC database. User will need to know native data type in order to input proper values.
Force update of any outstanding supertype 2 and 3 data changes
for jobName to DBEX. If jobName is "ALL*", forces update for all jobs.
Dump the hash table.
Dump the entire SLC database.
TBD: Database meta data as well as data values are output.
Note that output can be piped to a filename.
Returns the current database version in an 8 character string.
char *
dbgetVersion(void)
dbio.c contains implementation for the Database Input/Output Utilities . Please refer to the document for full details.