Introduction
The maintenance of statistics such as "packets transmitted" for a network interface can greatly assist the system administrator (or user) in a variety of circumstances.
Traditionally, support for such statistics gathering has been irregular and generally limited to direct screen printing. This specification provides a more controlled method of presenting statistics to the user, and permits a single generic display program to cope with all compliant modules.
The following characterise the intentions of this interface specification:
Plus points
- Simplicity of implementation
- Single generic statistics display program
- No essential central information that needs frequent updating
- Information is for presentation to humans primarily
- Atomic operations without any latching mechanisms
- Well behaved with multiple gatherers
- Well behaved if a supplier unexpectedly disappears
- Virtually stateless communication
Minus points
- Not re-entrant - callbacks or user mode code only
- Restrictive string model
- Polled interface, rather than event driven
- Only simple flat structure to statistics
- Only really suitable for human display
Technical Details
Basics of operation
A piece of software that wishes to offer statistics is termed a "supplier". A piece of software that obtains statistics from a supplier and presents them to the user is termed a "gatherer", and the operation it performs is often referred to as "enumerating the statistics". There are typically more suppliers than gatherers in a system.
As part of its normal operations, a supplier maintains a set of statistics. A gatherer, typically upon user invocation, enumerates the available suppliers, enumerates the statistics supplied by each supplier and then presents this information to the user. More dynamic presentation is also possible. A supplier is passive (other than its normal operations), whilst the gatherer actively seeks out the information to be presented.
The first stage of enumeration (determining what suppliers are present) is performed with a service call, and the second stage (determining what statistics are available and their values) is performed through a SWI whose SWI number is obtained during the first stage of enumeration. This SWI number is used for all stages of operation apart from enumeration itself.
Each statistic has a number of different pieces of information associated with it, forming its description, and a value. The description provides the necessary information to access and manipulate the value, including the number of bytes of storage required for the value, its type, format and desired presentation. The data types supported are boolean, integer and string. An unused data type also provides for padding.
Descriptions and statistics are read in ranges from a lower statistic number to an upper statistic number, inclusive. The first statistic of any supplier is zero. The highest statistic number of a supplier is obtained when the SWI number for a supplier is obtained. A range is always processed atomically by the supplier, ensuring consistent values and presentation to a gatherer when so required. A supplier indicates the volatility of its statistics and typically attempts to group statistics into ranges of the same volatility.
Enumerating suppliers
The suppliers are enumerated with Service_StatisticEnumerate. Each supplier allocates a small structure from the RMA, initialises it and links it into the linked list whose head is pointed to by R0. Whether the supplier adds an entry at the head or tail of the list does not matter. If a gatherer requires any specific order, then it should explicitly sort the suppliers list itself. A gatherer should not cache SWI values across invocations of suppliers, as a module might choose a dynamic SWI numbering scheme if it offers multiple effective suppliers.
In C, the structure is defined as follows:
typedef struct spctl
{
struct spctl *next; /* Next structure in list */
unsigned int i_version; /* Interface version */
unsigned long features; /* Combination of SF_ values */
unsigned int swinumber; /* The SWI SA_DESCRIBE/SA_READ use */
unsigned int max_stat; /* Highest stat number (inclusive) */
unsigned int type; /* Acorn assigned supplier type */
unsigned int s_version; /* Supplier version */
char *module; /* Module name (short one) */
char *title; /* Title string - short */
char *description; /* Descriptive string - long */
unsigned char reset[8]; /* Unique for each invocation */
} dci4_spctl;
and its fields are:
| Offset | Name | Contents |
|---|---|---|
| 0 | next | Pointer to the next structure in the list. This field is used to construct the linked list of suppliers for the enumeration operation. A value of zero indicates the end of the list. |
| 4 | i_version | The DCI4 Statistic Interface version that the supplier is implemented against. This is version 101. |
| 8 | features | A bitset of flags defining optional aspects of a supplier. Values are detailed elsewhere. |
| 12 | swinumber | The SWI number through which all other communication with the supplier is performed. |
| 16 | max_stat | The highest statistic number, inclusive, that the supplier provides. This implies a supplier must always supply at least one statistic. |
| 20 | type | This field is available to provide some classification of suppliers. It is the only centrally administered resource in this specification, and its use is optional. The currently defined values are given below. |
| 24 | s_version | This is the version number of the supplier module itself (as opposed to the version number of an interface it conforms to). It is provided for the convenience of the user. |
| 28 | module | Pointer to the supplier's module title (as opposed to its help string). |
| 32 | title | Pointer to a short descriptive string to identify the supplier to the user. The gatherer only contracts to print at least the first twenty characters of this string. |
| 36 | description | Pointer to a longer description of the supplier that should convey its purpose to the user. The gatherer only contracts to print at least the first fifty characters of this string. |
| 40 | reset[8] | This 8 byte field is initialised with a unique value each time the supplier is initialised. It permits the gatherer to spot a supplier that has just re-initialised. The time and date at which the supplier initialises are recommended values to write here. The gatherer performs an 8 byte equality test and otherwise assumes no further format information about these bytes. |
The supplier types currently defined are (expressed in C):
#define SPT_GENERAL_SUPPLIER 0 /* Use this if no other suitable type */
#define SPT_NETWORK_PROTOCOL 1 /* A DCI4 protocol module */
#define SPT_NETWORK_DRIVER 2 /* A DCI4 device driver module */
#define SPT_MBUF_MANAGER 3 /* The DCI4 mbuf manager module */
When a gatherer has finished interacting with a supplier, it should free the allocated structure back into the RMA.
Communicating with a supplier
Apart from the enumeration phase, all communication with a supplier is performed through the single SWI number obtained during the enumeration phase. The action performed by this "statistic SWI" is controlled by R0 on entry, which contains an action code. The defined action codes are:
SA_DESCRIBE 0
SA_READ 1
SA_DESCRIBE obtains descriptions of statistics, whilst SA_READ obtains the actual values of statistics. It is necessary to first obtain the description of a statistic before reading it, as the description is the only way the gatherer can obtain the size of the statistic value and know how much buffer space to allocate.
The register usage for both SA_DESCRIBE and SA_READ is very similar. In essence, both these actions enumerate information about a range of statistics into the buffer supplied. This is performed atomically, where necessary, by the supplier. In both cases, the operation starts from the first statistic and proceeds in steps of one and stops once the last statistic has been processed. Output is placed into the buffer supplied. If this buffer is not big enough for the requested range of statistics, then processing will stop when it is no longer possible to write all the necessary information for a statistic (ie if the buffer supplied is not big enough, the number of unused bytes will be less than the size of a statistic description). Return values of the number of statistics processed and the number of bytes of the buffer used are returned. The buffer must be word aligned in memory.
Statistic descriptions
Each statistic is described with a fixed sized buffer, described in C as follows:
typedef struct stdesc
{
int type; /* ST_ series */
unsigned int format; /* SxF_ series */
unsigned int presentation; /* SxP_ series */
unsigned int size; /* Measured in bytes */
unsigned int volatility; /* SV_ series */
char *name; /* String is static */
unsigned int name_tag; /* See specification */
unsigned int spare; /* Unused. Always zero */
} dci4_stdesc;
and its fields are:
| Offset | Name | Contents |
|---|---|---|
| 0 | type | The type of statistic (the ST_ series, given below). |
| 4 | format | A value indicating the format of the value. The range of values is specific to each type (all the integer types share a common set of formats and presentations for convenience). Depending upon the type in question, this value may be an enumeration, a bitset of flags or a combination of both (none of the currently defined formats mix enumeration and flag bitsets). |
| 8 | presentation | How the supplier would like the value to be presented. A gatherer may choose to ignore this value, but only at the risk of presenting the user with incoherent information. |
| 12 | size | The number of bytes the statistic wishes reserved in the buffer for an SA_READ operation. It is always a multiple of four bytes. It is sometimes larger than the actual value necessary. |
| 16 | volatility | How volatile a statistic is (the SV_ series, given below). |
| 20 | name | Pointer to the name of the statistic. It is a short descriptive string that the gatherer contracts to display at least the first twenty characters of. The string itself is static, zero byte terminated and contained with the supplier module. |
| 24 | name_tag | This value, together with the information contained in the dci4_spctl structure and a ISV/IHV supplied data file permit the gatherer to obtain a description of a statistic. The precise mechanism has yet to be determined. |
| 28 | spare | This field is reserved for future use and will be zero. |
The possible values of the type field are:
#define ST_UNUSED 0
#define ST_BOOLEAN 1
#define ST_STRING 2
#define ST_INTEGER8 3
#define ST_INTEGER16 4
#define ST_INTEGER32 5
#define ST_INTEGER64 6
#define ST_ADDRESS 7
#define ST_TIME 8
The three possible values of the volatility field are:
#define SV_STATIC 0 /* Constant per invocation */
#define SV_VARIABLE 1 /* Unlikely to have changed in 5 minutes */
#define SV_VOLATILE 2 /* Can change very rapidly */
Statistic types
ST_UNUSED
ST_UNUSED is used by a supplier to reserve a statistic number. Such a statistic always has a value occupying zero bytes, and hence can always be enumerated, no matter how many bytes are left in the caller supplied buffer. A statistic with a type of ST_UNUSED should always be skipped over when printing and enumerating - none of the fields beyond the type and size fields are valid for unused statistics.
ST_BOOLEAN
ST_BOOLEAN values are single bit boolean values. A variety of different presentation methods may be selected by the supplier in order to maximise coherence between the description and the presentation of a boolean value. Only the least significant bit of a boolean type has a defined value.
The formats which this type uses are:
| Format | Meaning |
|---|---|
| SBF_NORMAL (0) | The value supplied is 0 if boolean 'false' and any other value for boolean 'true'. |
| SBF_INVERTED (1) | The boolean state is inverted, 0 if boolean 'true' and any other value for boolean 'false'. |
The presentations which this type uses are:
| Presentation | Meaning |
|---|---|
| SBP_ON_OFF (0) | The strings 'on' and 'off' will be used. |
| SBP_YES_NO (1) | The strings 'yes' and 'no' will be used. |
| SBP_TRUE_FALSE (2) | The strings 'true' and 'false' will be used. |
| SBP_ALWAYS_NEVER (3) | The strings 'always' and 'never' will be used. |
| SBP_ONE_ZERO (4) | The strings '1' and '0' will be used. |
ST_STRING
ST_STRING values are string values. Currently, strings are defined using a zero byte as a terminator character (later versions may well introduce counted length strings as well). A supplier is required to be able to place an upper length value on all strings so that sufficient space may be allocated within the caller supplied buffer for the SA_READ operation.
The formats which this type uses are:
| Format | Meaning |
|---|---|
| SSF_ZEROTERM (0) | This is the only defined format, indicating that the string is zero-terminated. |
The presentations which this type uses are:
| Presentation | Meaning |
|---|---|
| SSP_LITERAL (0) | The string should be presented as supplied. |
ST_INTEGERx
ST_INTEGERx values are all integer values. A variety of different sized integers are supported to suit different requirements. A flexible set of presentation methods is supported.
The formats which this type uses are a bit field:
| Format | Meaning |
|---|---|
| SIF_UNSIGNED (0x01) | If this bit is set, the value is unsigned. If this bit is clear, the value is signed. |
| SIF_BIGENDIAN (0x02) | If this bit is set, the value is supplied as big-endian. If this bit is clear, the value is supplied as little-endian. |
The presentations which this type uses are:
| Presentation | Meaning |
|---|---|
| SIP_HEXADECIMAL (0) | The value should be presented as a hexadecimal number. |
| SIP_DECIMAL (1) | The value should be presented as a decimal number. |
| SIP_DOTTED (2) | The value should be presented separating every octet with a '.' character. This was intended for use for addresses; it has been superseded by the ST_ADDRESS type. |
ST_ADDRESS
ST_ADDRESS values are network address values. A number of different formats of address are supported. This type is only available in version 1.01 of the statistics interface.
The formats which this type uses are:
| Format | Meaning |
|---|---|
| SAF_ETHERNET (0) | The value is a 6 byte MAC address. A value of 0 indicates no address. |
| SAF_IP (1) | The value is a 4 byte IP address. A value of 0 indicates no address. |
| SAF_ECONET (2) | The value is a 2 byte (net.station) address. |
The presentations which this type uses are:
| Presentation | Meaning |
|---|---|
| SAP_NORMAL (0) | There is only this presentation type. |
ST_TIME
ST_TIME values are times when events occurred, or will occur. This type is only available in version 1.01 of the statistics interface.
The formats which this type uses are:
| Format | Meaning |
|---|---|
| STF_5BYTE (0) | The value is a standard 5 bytes format (8 bytes, top 3 bytes ignored), measured in centiseconds since 00:00 1st January 1900. |
| STF_CTIME (1) | The value is a C time() value, measured in seconds since 00:00 1st January 1970. |
| STF_MONOTONIC (2) | The value is a centisecond value, as used by OS_ReadMonotonicTime, measured in centiseconds since the system started. |
The presentations which this type uses are:
| Presentation | Meaning |
|---|---|
| STP_ABSOLUTE (0) | The values are absolute and should be presented as time values from their reference base. An absolute value of 0 indicates that no time has been specified. |
| STP_RELATIVE (1) | The values are relative to the current time, and the reference base should be ignored. For example, a value of 250 in format STF_MONOTONIC might be presented as '2.50 seconds'. A relative value of 0 indicates that no period has been given. |
Ping pong atomicity
The supplier is required to perform each read request atomically. This means a gatherer can rely upon a read operation producing consistent statistics, but it cannot be certain quite when in time these statistics apply to; in some cases, statistics are virtually always out of date by the time the gatherer obtains and uses them.
The simplistic approach to ensuring an atomic read operation is to perform the entire action with interrupts disabled. This, however, has an undesirable effect on interrupt latency for suppliers with many or complex statistics and is not recommended.
The suggested method is a three buffer scheme. One buffer holds the accumulated statistics, the second holds the increments (deltas) being generated "live", and the third is either entirely full of zero values, or there is an active read operation in progress and it holds a previous set of increments that are currently being added (merged) into the accumulated values.
Two pointers are maintained. One for the "live" buffer and the other for the "merge" buffer. The operation that is necessary to perform atomically is the swapping of these two pointer values.
The overall structure suggested is as follows:
start thread of control (ie SWI)
enter mutex (eg disable interrupts)
temp := buffer pointer 1
buffer pointer 1 := buffer pointer 2
buffer pointer 2 := temp
exit mutex (and ensure interrupts enabled)
add values from buffer pointer 2 into the accumulated values
zero the memory described by buffer pointer 2
end thread of control (ie SWI)
This maximises the usefulness of the non re-entrancy requirement and requires ONLY the swapping of the buffer pointers to be performed atomically. All other operations can proceed with interrupts enabled. This minimal critical region is the prime motivation behind this scheme.
Additional notes
Some applications of the statistic interface will require a single RISC OS module to behave as multiple suppliers. For example, a device driver module that controls multiple interfaces might well wish to provide an overall set of statistics, and then an instance of a set of statistics per interface controlled. A separate SWI number is required for each 'effective supplier'. This is not normally a problem, as very few modules approach the limit of the 64 separate SWIs allocated. Gatherers are expected to obtain SWI numbers through the enumeration method supplied, and should be capable of handling dynamic SWI number assignment. For example, a device driver may choose SWIs 63, 62 and 61 for units 0, 1 and 2, respectively. If a gatherer were to attempt to 'cache' these SWI numbers across invocations of the device driver, then it might not correctly cater for the addition or removal of interfaces.
Service Calls
| R0 | = | 0 |
| R1 | = | &A1 (Service_StatisticEnumerate) |
| R0 | = | NULL terminated head of linked list of structures |
| R1 | preserved | |
Each supplier allocates a small structure from the RMA, initialises it and links it into the linked list whose head is pointed to by R0. See Enumerating suppliers for the structure.
Entry points
| R0 | = | 0 (SA_DESCRIBE) |
| R1 | = | First statistic, inclusive |
| R2 | = | Last statistic, inclusive |
| R3 | = | First byte of buffer in memory |
| R4 | = | Number of bytes in buffer |
| R5 | = | Number of statistics processed |
| R6 | = | Number of bytes of buffer used |
This is the action of the statistic SWI (the SWI whose number is the swinumber of the supplier) which writes a dci4_stdesc structure for each statistic in the range into the buffer. The description of a statistic must be read before its value, as it gives the size of the value. The buffer must be word aligned, and the SWI is not re-entrant.
| R0 | = | 1 (SA_READ) |
| R1 | = | First statistic, inclusive |
| R2 | = | Last statistic, inclusive |
| R3 | = | First byte of buffer in memory |
| R4 | = | Number of bytes in buffer |
| R5 | = | Number of statistics processed |
| R6 | = | Number of bytes of buffer used |
This is the action of the statistic SWI which writes the value of each statistic in the range into the buffer, using the number of bytes given in the size field of its description. The range is read atomically by the supplier. The buffer must be word aligned, and the SWI is not re-entrant.