|
|
Buffer Management
13.1 Overview
The service calls in this chapter are used to manage the data buffers that are used to carry "application" or "wire" data within the UDI environment. Any form of data transfer either to or from the device or between UDI modules will use a UDI buffer construct to reference that data.
In order to facilitate various device and DMA requirements and to avoid copying data, UDI buffers implement a layer of abstraction between the driver and the actual data. A device driver does not typically need to access data with the exception of various headers or tags, so the lack of direct access to the data is typically not even noticed in the UDI driver.
Using this abstraction, drivers are presented with a "logical" view of the data as a single contiguous block of data accessible via UDI buffer read/write operations. The implementation of the UDI buffer is determined by the UDI environment implementation and a single UDI environment may have several different buffer implementations supporting the UDI driver-to-buffer interface. This facility allows buffer data to be distributed into multiple virtual and physical segments as needed and desired to achieve the aforementioned goals of copy avoidance and natural DMA presentation.
Another valuable effect of representing buffers logically rather than using direct virtual access is that data may be added to or removed from any part of the buffer without requiring extra copy or buffer chaining operations. New sections of data may be chained into the existing buffer "behind the scenes" by the environment without disturbing the present buffer contents. Likewise the environment can adjust the buffer's representation to ignore deleted portions of data without requiring the actual data to be rewritten. The extent to which these practices are performed is determined entirely by the UDI environment implementation; the driver is not concerned with these minutiae.
No endianness conversion is performed on the data in UDI buffers when they are transferred between regions. UDI buffer data is managed by the environment as an untyped string of bytes.
13.2 Buffer Type
UDI buffers are represented by the following semi-opaque type.
NAME udi_buf_t
Logical buffer type
#include <udi.h>typedef struct { udi_size_t buf_size; } udi_buf_t;MEMBERS buf_size is the current size of the buffer data, in bytes. The environment will adjust this as necessary as a result of service calls that change the buffer's content. The driver may also change the value, to indicate a desired size change, which will affect subsequent service calls.
description The udi_buf_t structure is used to reference a collection of data that is passing through a driver, typically between an application and a device or communications medium.
The udi_buf_t structure is a semi-opaque type, and must only be allocated by environment service calls. UDI buffers are transferable between regions.
If a buffer is used with a service call that retrieves the contents of some or all of the buffer data (such as udi_buf_read or udi_buf_copy) and the buf_size value is larger than the extent of data explicitly written into the buffer, the values retrieved for the un-written range are unspecified.
If data is written into the buffer (such as with udi_buf_write or udi_buf_copy), and the starting offset at which the data is written is greater than the extent of data previously explicitly written into the buffer, an values subsequently retrieved for the un-written range are unspecified.
Any service call that potentially modifies a buffer's contents returns a new buffer pointer in the corresponding callback. While this pointer may in many cases be equal to the original buffer pointer, the environment may in fact have reallocated the buffer, so drivers must always replace all subsequent use of the original buffer pointer with the new pointer. This is also true of buffers passed to channel operations.
13.3 Transfer Constraints
UDI buffer allocation and usage is subject to various constraints specifications. This section describes those constraints that relate to data transfer operations. The UDI Physical I/O Specification specifies additional constraints related to DMA operations.
NAME udi_xfer_constraints_t
Transfer constraints structure
#include <udi.h>typedef struct { udi_ubit32_t udi_xfer_max; udi_ubit32_t udi_xfer_typical; udi_ubit32_t udi_xfer_granularity; udi_boolean_t udi_xfer_one_piece; udi_boolean_t udi_xfer_exact_size; udi_boolean_t udi_xfer_no_reorder; } udi_xfer_constraints_t;MEMBERS udi_xfer_max is the maximum # of bytes for an I/O transfer that can be supported by the device and/or driver. Zero indicates that there is no restriction on transfer size.
udi_xfer_typical is the typical # of bytes for an I/O transfer to this device. This value may be used by the environment to optimize pre-allocation decisions. Zero indicates that the device has no typical pattern. Only drivers that do have a typical pattern should set this attribute. This constraint is typically used to assist the environment in implementing pre-allocation strategies.
udi_xfer_granularity is the transfer granularity. The total transfer size must be a multiple of this number of bytes. For random access devices, it is also required that the starting device offset for a transfer must be a multiple of the transfer granularity. A value of one effectively means no restriction. Transfer size is a function of metalanguage-specific operations, and may or may not be related to the size of buffers used to pass the data.
udi_xfer_one_piece is a flag indicating (if TRUE) that the transfer must be handled as a single request; it cannot be broken up. This is typically used for drivers that use the transfer size as an implicit attribute; for example, a tape driver might use the transfer size to control the size of the block written to a tape. Also acts as if UDI_XFER_EXACT_SIZE were TRUE.
udi_xfer_exact_size is a flag indicating (if TRUE) that transfer requests that don't conform to transfer granularity constraints must be failed instead of being passed to the driver. Even if this flag is not set, the request that is passed to the driver will still meet the transfer granularity constraints, but it may have been modified from the original request in order to do so (using a blocking/de-blocking algorithm).
udi_xfer_no_reorder is a flag indicating (if TRUE) that transfer requests must be passed to the driver in FIFO order. Any fine-grained breakup into smaller requests must also preserve ascending device offset order and must not insert new requests into the stream.
DESCRIPTION The udi_xfer_constraints_t structure is used to describe the various transfer constraints for a specific operation. These transfer constraints may be passed to a child via a metalanguage-specific bind acknowledgement channel operation to communicate the transfer requirements to the child driver; a metalanguage may alternatively explicitly specify an applicable subset of these constraints in a manner unique to that metalanguage.
The transfer constraints information may be used by a driver to determine how to divide requests into appropriate individual control blocks and buffers for handling by the parent driver.
13.4 Buffer Management Macros
The macros specified in this section are standard buffer management macros provided for convenience in using the buffer management service calls. These macros are built on top of the buffer management service calls in Section 13.5.
NAME UDI_BUF_ALLOC
Allocate and initialize a new buffer
#include <udi.h>#define \ UDI_BUF_ALLOC( \ callback, gcb, init_data, size, path_handle) \ udi_buf_write(callback, gcb, init_data, \ size, NULL, 0, \ 0, path_handle)ARGUMENTS callback, gcb are standard arguments described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions".
init_data is a pointer to the initial data to use to fill the buffer. If set to NULL, the initial data values are unspecified.
size is the initial size of the buffer data, in bytes.
path_handle is the handle identifying the intended use and dispatching of this buffer. Path handle usage is determined by the driver, but by associating the use of a specific path_handle with buffers allocated for a specific purpose, the driver allows the environment to predict and optimize the allocated buffer requirements.
DESCRIPTION UDI_BUF_ALLOC allocates a new logical buffer with a valid data length of size. The initial data will be copied from init_data if non-NULL. If init_data is NULL, the buffer will still have size bytes of valid data, but the initial value of these bytes is unspecified.
The macro UDI_BUF_ALLOC must be called as if it had the following functional interface, as can be derived from the above macro definition and the definition of udi_buf_write:
void UDI_BUF_ALLOC ( udi_buf_write_call_t *callback, udi_cb_t *gcb, void *init_data, udi_size_t size, udi_buf_path_t path_handle ); typedef void udi_buf_write_call_t ( udi_cb_t *gcb, udi_buf_t *new_buf );NAME UDI_BUF_INSERT
Insert bytes into a logical buffer
#include <udi.h>#define \ UDI_BUF_INSERT( \ callback, gcb, new_data, size, \ dst_buf, dst_off) \ udi_buf_write(callback, gcb, new_data, \ size, dst_buf, dst_off, \ 0, UDI_NULL_BUF_PATH)ARGUMENTS callback, gcb are standard arguments described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions".
new_data is a pointer to the new data bytes to insert into the buffer data. If set to NULL, the size bytes inserted into dst_buf at dst_off shall have unspecified values.
size is the number of bytes to insert into dst_buf.
dst_buf is a handle to the logical buffer into which to insert bytes.
dst_off is the logical offset from the first valid data byte in the buffer to the start of the insertion, in bytes.
DESCRIPTION UDI_BUF_INSERT inserts size bytes into dst_buf at offset dst_off, logically moving any data currently at dst_off "down" by size bytes.
The macro UDI_BUF_INSERT must be called as if it had the following functional interface, as can be derived from the above macro definition and the definition of udi_buf_write:
void UDI_BUF_INSERT ( udi_buf_write_call_t *callback, udi_cb_t *gcb, void *new_data, udi_size_t size, udi_buf_t *dst_buf, udi_size_t dst_off ); typedef void udi_buf_write_call_t ( udi_cb_t *gcb, udi_buf_t *new_dst_buf );NAME UDI_BUF_DELETE
Delete bytes from a logical buffer
#include <udi.h>#define \ UDI_BUF_DELETE( \ callback, gcb, size, dst_buf, dst_off) \ udi_buf_write(callback, gcb, NULL, \ 0, dst_buf, dst_off, \ size, UDI_NULL_BUF_PATH)ARGUMENTS callback, gcb are standard arguments described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions".
size is the number of bytes to delete from dst_buf.
dst_buf is a handle to the logical buffer from which to delete bytes.
dst_off is the logical offset from the first valid data byte in the buffer to the start of the deletion, in bytes.
DESCRIPTION UDI_BUF_DELETE deletes size bytes from dst_buf starting at offset dst_off, logically moving any additional data "up" to fill the gap.
The macro UDI_BUF_DELETE must be called as if it had the following functional interface, as can be derived from the above macro definition and the definition of udi_buf_write:
void UDI_BUF_DELETE ( udi_buf_write_call_t *callback, udi_cb_t *gcb, udi_size_t size, udi_buf_t *dst_buf, udi_size_t dst_off ); typedef void udi_buf_write_call_t ( udi_cb_t *gcb, udi_buf_t *new_dst_buf );NAME UDI_BUF_DUP
Copy a logical buffer in its entirety
#include <udi.h>#define \ UDI_BUF_DUP( \ callback, gcb, src_buf, path_handle) \ udi_buf_copy(callback, gcb, src_buf, \ 0, (src_buf)->buf_size, \ NULL, 0, 0, path_handle)ARGUMENTS callback, gcb are standard arguments described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions".
src_buf is a handle to the logical buffer to copy.
path_handle is the handle identifying the intended use and dispatching of this buffer. Path handle usage is determined by the driver, but by associating the use of a specific path_handle with buffers allocated for a specific purpose, the driver allows the environment to predict and optimize the allocated buffer requirements.
DESCRIPTION UDI_BUF_DUP makes a logical copy of src_buf and passes the new buffer to the driver with a callback.
The macro UDI_BUF_DUP must be called as if it had the following functional interface, as can be derived from the above macro definition and the definition of udi_buf_copy:
void UDI_BUF_DUP ( udi_buf_copy_call_t *callback, udi_cb_t *gcb, udi_buf_t *src_buf, udi_buf_path_t path_handle ); typedef void udi_buf_copy_call_t ( udi_cb_t *gcb, udi_buf_t *new_dst_buf );13.5 Buffer Management Service Calls
The functions in this section provide basic UDI buffer management services. These services include the ability to copy one UDI buffer to another, to transfer (read and write) data bytes between driver memory and a UDI buffer, and to free a UDI buffer. A UDI buffer may be allocated by copying or writing without an initial buffer (e.g., see the UDI_BUF_ALLOC macro).
13.5.1 Buffer Usage Models
The UDI buffer is used to pass user data from one UDI region to another, typically for the purpose of performing I/O with that buffer. This I/O path may involve several layers of either native OS or UDI modules and some of those modules may wish to implement "retransmit" functionality based on various conditions such as timeouts or failed acknowledgements. Buffer management therefore needs to be implemented in a highly efficient manner. Native OS buffer handling has been optimized over time to avoid copying or relocating data during the high-performance paths in the driver. UDI allows the same types of optimization to be performed as part of the environment implementation although the specification of how the metalanguage manages these buffers is a critical part of this model.
Most I/O designs can be roughly grouped into one of two buffer models:
- The command/response model where there is no asynchronous or unsolicited data from the device, and
- The push model where data is pushed from either end but there's no direct "acknowledgement" or "completion" of that data transfer.
The most typical example of the command/response model is the SCSI storage protocol. In this protocol, the application supplies the data buffer that either contains data to be written to the device or specifies a buffer region into which data is to be read from the device. The buffer is associated with a command which instructs the adapter and remote device to perform the data transfer, and a response which indicates the success or failure of that transfer. Any retransmissions are usually as a result of a failure indication for the transfer.
The common example of a push model is a network protocol. For most (LAN-based) network protocols, the application supplies a buffer which is manipulated by various protocol entities and then transmitted on a best-case basis. Various amounts of lossage are expected and protocols or applications are typically constructed to expect this lossage and initiate retransmissions if the data is not acknowledged within a specific period of time. Likewise, incoming data may arrive asynchronously and unsolicited from any network partner and may need to be delivered to any one or more applications after appropriate protocol processing.
As a general rule, UDI metalanguages will manage the usage of UDI buffers based one of the above buffer models:
- For a command/response model, the buffer will be passed down to the UDI driver along with the initial command and the (possibly modified) buffer will be passed back with the response. On write failures, metalanguages generally require the driver to pass the buffer back with its contents unmodified; this allows the requester to retransmit the buffer if it so desires.
- For the push module, the buffer is passed down with the request and always deallocated by the UDI driver after being transmitted, regardless of the success or failure of the transmission. If an upper level module wishes to implement a retransmit algorithm based on timers or remote acknowledgements, it must create a copy of the buffer before passing it to the lower level driver.
It is important at this point to note that the "copy" of the buffer is not necessarily a full copy of the data portion. The UDI environment may simply create another buffer handle that refers to the same data for the copy; this is implementation dependent and is acceptible as long as the environment insures that any buffer modifications through one handle are not visible through another handle (usually by performing a "late-copy" at the time the modification occurs).Each UDI metalanguage is free to manage buffers in a manner appropriate to that metalanguage (and may even manage different buffers in a different manner for different metalanguage operations) but must specify the methodology to be used in the metalanguage specification and as part of the metalanguage library interface.
13.5.2 Buffer Recovery Mechanism
For most situations in the UDI environment, ownership of a resource such as a buffer is passed to the target region whenever the corresponding handle is passed to that target as part of a metalanguage operation. Any module wishing to preserve the data will typically create a copy of the buffer as described above.
However, in the command/response buffer usage model, the buffer is not copied by the child UDI module before being passed to the parent for processing. Instead, the child module expects the parent to return the buffer when an error occurs. Under normal operating conditions the parent can satisfy this expectation but in the event of an abrupt removal of the parent device (e.g. a hot swap condition) the parent will be unable to return the buffer to the child.
In this situation the child still needs the buffer returned to it in order to perform retransmissions or perhaps perform a failover operations. This is supported in UDI through the operation recovery mechanism described in Section 4.10, "Driver Faults/Recovery". In this situation, the UDI environment will return any buffers held by the parent region to the child as part of the recovery process. Each metalanguage specification shall indicate which operations and their associated buffers are handled in this manner.
NAME udi_buf_copy
Copy data from one logical buffer to another
#include <udi.h>void udi_buf_copy ( udi_buf_copy_call_t *callback, udi_cb_t *gcb, udi_buf_t *src_buf, udi_size_t src_off, udi_size_t src_len, udi_buf_t *dst_buf, udi_size_t dst_off, udi_size_t dst_len, udi_buf_path_t path_handle ); typedef void udi_buf_copy_call_t ( udi_cb_t *gcb, udi_buf_t *new_dst_buf );ARGUMENTS callback, gcb are the standard arguments described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions"
src_buf is a pointer to the buffer containing data to be copied. This must not be set to NULL.
src_off is the offset, in bytes, from the first logical data byte to the start of the copy area in the source buffer. This must not exceed the current size of the buffer:
0 £ src_off < src_buf->buf_size
src_len is the number of bytes to be copied from the source buffer. src_len must be at least 1, and src_off + src_len must not extend beyond the current buffer size:
0 < src_len £ (src_buf->buf_size - src_off)
For src_len of zero, use udi_buf_write instead.
dst_buf is a pointer to the buffer that is the target of the data copy. If set to NULL, a new, empty buffer will be allocated before copying the source data.
dst_off is the offset, in bytes, from the first logical data byte to the start of the copy area in the destination buffer. The buffer will be extended if necessary to accommodate the data.
0 £ dst_off £ dst_buf->buf_size
dst_len is the number of bytes in dst_buf to be replaced with data copied from the source buffer.
0 £ dst_len £ (dst_buf->buf_size - dst_off)
If dst_buf is NULL, both dst_off and dst_len must be zero.
path_handle is the handle identifying the intended use and dispatching if a new buffer must be allocated for this request. Path handle usage is determined by the driver, but by associating the use of a specific path_handle with buffers allocated for a specific purpose, the driver allows the environment to predict and optimize the allocated buffer requirements. If dst_buf is not NULL on entry, its existing path will continue to be used and this parameter must be set to UDI_NULL_BUF_PATH; otherwise it must be non-null.
new_dst_buf is a pointer to the new, modified destination buffer.
DESCRIPTION udi_buf_copy logically replaces dst_len bytes of data starting at offset dst_offset in dst_buf with a copy of src_len bytes of data starting at src_offset in src_buf. When the data has been copied, the callback routine is called.
If dst_len is zero, the src_len bytes of source data will be inserted in the destination buffer at dst_off. If dst_len is positive, the dst_len bytes will be replaced by src_len bytes from the source buffer. The src_len parameter must be > 0 bytes. (For a src_len of zero, udi_buf_write must be used.)
This routine is very similar to udi_buf_write, except that the data source is another buffer, rather than a virtually-contiguous data structure.
If dst_buf is NULL, a new buffer will be allocated to hold the data.
The destination buffer will be extended or reallocated as necessary to hold any new data being added to the buffer. This extension or reallocation is performed by the environment as part of the udi_buf_copy operation and all data in the destination buffer not described by the dst_off and dst_len region will be preserved. This reallocation may result in a new buffer being returned in the callback, therefore the dst_buf should no longer be used after passing it to udi_buf_copy and the driver must use the new_dst_buf value following the callback.
It is expected that this routine will efficiently duplicate buffers (e.g., when multiple higher levels above a multiplex point must receive the same inbound buffer). Because UDI implementations may avoid copying data whenever possible, the actual allocation of space for the copied data may be delayed until the shared data is written via either buffer.
WARNINGS Control block usage must follow the rules described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions".
src_buf and dst_buf must not reference the same buffer.
On successful completion, dst_buf will no longer be valid and new_dst_buf is substituted, even if dst_buf was not specified as NULL. new_dst_buf may be set to the same handle value as the input value of dst_buf, but the driver must not depend on this.
If this operation is cancelled with udi_cancel, any pre-existing dst_buf buffer will be discarded (see udi_cancel for an explanation of why this is so).
REFERENCES udi_buf_write, udi_cancel
NAME udi_buf_write
Write data bytes into a logical buffer
#include <udi.h>void udi_buf_write ( udi_buf_write_call_t *callback, udi_cb_t *gcb, const void *src_mem, udi_size_t src_len, udi_buf_t *dst_buf, udi_size_t dst_off, udi_size_t dst_len, udi_buf_path_t path_handle ); typedef void udi_buf_write_call_t ( udi_cb_t *gcb, udi_buf_t *new_dst_buf );ARGUMENTS callback, gcb are standard arguments described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions".
src_mem is a pointer to caller memory where the first byte of data is to be copied from. If NULL, the resulting data values are unspecified.
src_len Number of bytes to be copied from src_mem, replacing the specified dst_len bytes in dst_buf. If src_mem is NULL the dst_len bytes in dst_buf are replaced by src_len bytes of unspecified data values. If src_len is zero, src_mem is ignored.
dst_buf are the same arguments as used in udi_buf_copy.
DESCRIPTION udi_buf_write copies data bytes from virtually contiguous driver memory area to a logical buffer. This function works like udi_buf_copy except that the data source is a virtually-contiguous memory area, rather than another buffer. No endianness conversion will be performed by udi_buf_write.
If src_mem is NULL, data in the resulting range of the destination buffer will have unspecified values. This is useful for ensuring that a buffer is instantiated to a certain size, without taking the expense of copying data into the buffer. This mechanism should only be used when the instantiated data must exist.
WARNINGS A NULL src_mem with nonzero src_len and dst_len can produce unspecified data values in the middle of valid data (e.g., src_mem=NULL, src_len=6, and dst_len=4 produces at least two bytes of unspecified data within the valid data area of dst_buf). While this is a legal operation, the results may be unexpected.
Control block usage must follow the rules described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions".
Use of the src_mem parameter must conform to the rules described in Section 5.2.1.1, "Using Memory Pointers with Asynchronous Service Calls".
If this operation is cancelled with udi_cancel, any pre-existing dst_buf buffer will be discarded (see udi_cancel for an explanation of why this is so).
REFERENCES udi_buf_copy, udi_cancel
NAME udi_buf_read
Read data bytes from a logical buffer
#include <udi.h>void udi_buf_read ( udi_buf_t *src_buf, udi_size_t src_off, udi_size_t src_len, void *dst_mem );ARGUMENTS src_buf is a pointer to a buffer containing data to be read.
src_off is the offset, in bytes, into the logical data of src_buf at which to start reading data.
src_off must be £ src_buf->buf_size.src_len The number of bytes to be read from src_buf.
src_off+src_len must not exceed src_buf->buf_size.dst_mem pointer to caller's memory where data is to be copied.
DESCRIPTION udi_buf_read non-destructively reads data bytes from a logical buffer to a virtually contiguous driver memory area pointed to by src_buf. No endianness conversion will be performed by udi_buf_read.
If src_buf->buf_size was previously extended to include bytes not explicitly written, the resulting values in dst_mem for these bytes are unspecified.
NAME udi_buf_free
Free a logical buffer
#include <udi.h>void udi_buf_free ( udi_buf_t *buf );ARGUMENTS buf is a pointer to the buffer to be deallocated. If buf is NULL on entry, this routine is a no-op.
DESCRIPTION udi_buf_free is called to indicate that a UDI buffer is no longer needed. The buffer and all associated resources will be released and the caller must no longer use the buffer handle, buf.
If buf is equal to NULL, explicitly or implicitly (zeroed by initial value or by using udi_memset), this function acts as a no-op. Otherwise, buf must have been allocated by udi_buf_copy or udi_buf_write, or passed to the driver via a channel operation.
REFERENCES udi_buf_copy, udi_buf_write
13.6 Buffer Paths
UDI buffers are used to transport data between various UDI modules for processing by those modules. Ultimately, if this data is destined for a physical device, the buffer will be passed to a physical I/O driver so the data can be presented to or retrieved from the associated hardware device, often via a DMA mechanism. (For more information on physical I/O drivers and DMA, see the UDI Physical I/O Specification.)
The various physical I/O devices and I/O buses in the system may have various DMA constraints and capabilities. To avoid additional processing overhead, it is desireable to ensure that the buffers presented for DMA processing are already conformant to the constraints and capabilities of the associated DMA engine. Within UDI, this is implemented by the environment by association with a buffer path handle.
Each time a buffer is allocated by a UDI module, a buffer path is specified for that allocation request (even if the module itself is not involved in the DMA operation). The path handle that is used for the allocation request should be associated with the expected path through the UDI modules that the buffer is likely to take: ideally, buffers presented to different DMA engines will have been allocated with different path handles.
When a buffer is mapped for DMA, the environment may, if it so chooses, update the associated buffer path object (remembered in the buffer handle from the original allocation) with the constraint and capability information of the DMA engine. By accumulating the most-restrictive combination of capabilities in the path object, the environment can optimize future allocations made with the corresponding path handle to ensure that newly-allocated buffers already conform to the accumulated DMA constraints and capabilities, avoiding subsequent reallocations and copies.
The buffer path mechanism is an optimization provided by UDI for performance improvements in DMA and buffer management. The module allocating a buffer is not required to use different path handles and likewise the UDI environment is not required to update the constraints associated with those path handles; the UDI specification requires the UDI environment to perform the needed buffer adjustments at the time that the buffer is mapped if it does not already conform to the DMA constraints, so any buffer may be passed along any "path" at the cost of the loss of these optimizations.
The UDI module allocating a buffer should choose a path handle based on the information available to it. It is valid to pass buffers allocated with different handles to the same parent (and ultimately the same DMA engine), and it is also valid to pass a buffers allocated with a single path handle to different DMA engines; however, the more closely the module can associate a path handle with a destination DMA engine the better the optimization opportunities for the UDI environment. Examples of buffer path selection heuristics include: the parent channel to which a multiplexing module passes the buffer, the destination IP address for an IP or TCP module allocating a network packet buffer, or the controller number for a SCSI command buffer.
13.6.1 Buffer Path Multiplexing
For a UDI multiplexer module with multiple parents, an additional facility is provided to assist in selecting the parent to which a buffer is passed. If the multiplexer has evaluated the various parents to which a particular buffer could be passed according to the implementation of that multiplexer and has arrived at a list of more than one possible parent, it may be advantageous for the multiplexer to pass the buffer to the parent whose DMA engine (or whose penultimate parent's DMA engine) is most capable of handling that buffer.
In this situation, the path handle is used in a slightly different manner than for buffer allocation. The multiplexer will typically maintain a path handle for each parent channel, internally maintaining a one-to-one association between a specific path handle and the corresponding parent channel. When the list of possible parents has been determined by the multiplexer by internal means, the udi_buf_best_path service may be called with the buffer and the list of path handles corresponding to the list of possible parent channels. The UDI environment will then select one or more of the paths to which the buffer should be passed, presumably based on the constraints associated with the specified paths.
The udi_buf_best_path service will return an array of indices into the path handle array, where the returned indices represent the best path or paths to which the buffer may be passed. The environment must return at least one path, but may determine that multiple paths are equivalent (or roughly equivalent) and therefore return an array of more than one indices. The UDI driver must also pass in the index of the most-recently used path; the UDI environment will begin selecting paths at the array position following the previously-matched index (wrapping as necessary) and terminating the search when it has reached the previously-matched index (which may also be included in the returned array of valid indices). If the environment continually finds multiple matches for buffers, the use of the previous index value will cause the first return match to indicate a round-robin algorithm for equitable load balancing scenarios.
As with the path handles used for buffer allocation, the UDI environment may choose how much information to maintain and update for the path handles used with udi_buf_best_path, and may, at one extreme, treat all paths as equally good, regardless of actual costs.
NAME udi_buf_best_path
Select best path(s) for a data buffer
#include <udi.h>void udi_buf_best_path ( udi_buf_t *buf, udi_buf_path_t *path_handles, udi_ubit8_t npaths, udi_ubit8_t last_fit, udi_ubit8_t *best_fit_array ); /* Terminator for best_fit_array */#define UDI_BUF_PATH_END 255ARGUMENTS buf is a pointer to a UDI data buffer.
path_handles is an array of candidate buffer path handles which correspond to parent instances to which the buffer might be sent..
npaths is the number of entries to use from the path_handles array. npaths must be greater than zero and less than 256.
last_fit is an index into the path_handles array (starting from zero) indicating the least preferred choice (typically, the one that was selected last time). last_fit must be less than npaths.
best_fit_array is an array of index values, which is filled in with the indices of one or more path_handles entries that best fit the data buffer. The list is terminated with an entry containing UDI_BUF_PATH_END. best_fit_array must point to enough space for (npaths+1) entries.
DESCRIPTION udi_buf_best_path is used to choose between multiple alternative path handles, each associated with a particular data path over which a request might be sent, and find those that can be expected to result in the best performance, all other aspects of the data path being equal. The environment may consider multiple choices to be equally suitable, and thus the result is returned as a list, in best_fit_array.
Some drivers may wish to factor in other criteria to further narrow down the choice; such drivers would scan the entire returned list. Others may simply take the first entry in best_fit_array, unconditionally. In the latter case, these drivers may want to load-balance among the otherwise-equal alternatives; this is achieved by setting last_fit to the index that was chosen in the previous call to udi_buf_best_best_path.
The index values returned in best_fit_array are provided in ascending order starting from the first one that is strictly greater than last_fit modulo npaths, and wrapping around once npaths is reached.
references UDI_BUF_ALLOC, udi_channel_event_cb_t
NAME udi_buf_path_alloc
Buffer path handle allocation
#include <udi.h>void udi_buf_path_alloc ( udi_buf_path_alloc_call_t *callback, udi_cb_t *gcb ); typedef udi_buf_path_alloc_call_t ( udi_cb_t *gcb, udi_buf_path_t new_buf_path );ARGUMENTS callback, gcb are standard arguments described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions".
new_buf_path is a newly allocated buffer path handle.
DESCRIPTION The udi_buf_path_alloc service is used to allocate a new buffer path handle to be used for describing a new buffer path. Buffer path usage is defined by the driver performing the allocation operation.
REFERENCES udi_buf_copy, udi_buf_path_t
NAME udi_buf_path_free
Buffer path handle deallocation
#include <udi.h>void udi_buf_path_free ( udi_buf_path_t buf_path );ARGUMENTS path is a buffer path handle to be deallocated.
DESCRIPTION The udi_buf_path_free call is used to deallocate a buffer path handle when it will no longer be used by the driver.
REFERENCES udi_buf_path_alloc, udi_buf_copy, udi_buf_path_t
13.7 Buffer Tags
Along with the actual buffer data content, there may be additional information related to a buffer that needs to be maintained along with that buffer and available to any UDI driver that is currently operating on the buffer. This is done by attaching one or more buffer tags to a UDI buffer. These buffer tags are used to provide additional descriptions of the data contained in the buffer without placing those descriptions in the data of the buffer itself.
Each buffer tag specifies the tag type, the portion of the buffer to which the tag applies, and the value (if any) associated with that tag. A buffer may have zero or more tags attached to that buffer and the tags may overlap, even for tags of the same type (although two tags that specify the exact same type and identify the same portion of the buffer will be reduced to a single tag whose value is that of the latter tag assignment). The tag will remain associated with the buffer until the buffer is deleted or until the tag is invalidated.
Buffer tags are related to specific data within the buffer and are used to describe that data. Because of this relationship, a tag will always indicate the same section of data in a buffer regardless of insertions or deletions before or after that section of the buffer. If the section of the buffer described by the tag is directly modified, the tag (along with all other tags associated with that buffer section) is invalidated and will be removed. Because of this behavior, tags should not be used to communicate critical information unless the UDI modules can provide assurances that the buffer will not be modified.
There is no limit to the number of tags that may be assigned to a buffer.
When a buffer is copied to another buffer or to a newly created buffer, any tags contained entirely within the copied section are duplicated in the destination buffer automatically.
13.7.1 Buffer Tag Categories
Buffer tags are divided into a number of categories which are used to assist in examining and processing the tags. The specific meaning and appropriate handling of a tag is defined individually for each tag; however, tags can be grouped into categories where the tags in each category perform related functionality. The following tag categories are defined:
- Value Tags. These tags are used to store a numeric value associated with the portion of the buffer that the tag applies to. A common example of this is a checksum value.
- Update Tags. These tags are used to request an update of the buffer based on a computation or scan of the associated portion of the buffer. The tag value for these tags usually represents a location in the buffer where the result of the computation or scan is to be written. A common example of this category of tag is for calculating a buffer data checksum and writing the result into a buffer header.
- Status Tags. These tags are used to indicate the status of the associated portion of the buffer. These tags are useful when the hardware is able to supply additional status information about buffer data that may need to be communicated to other modules. Status tags should not be used to store critical status due to the transitory nature of tags.
- Driver-internal Tags. These tags are defined and processed by UDI drivers and are ignored by the UDI environment. This category of tags may be used by the driver to store temporary information or inter-region information. This category of tags is driver-specific and driver-internal tags set by one driver will not be visible to any other driver that the buffer is passed to.
NAME udi_tagtype_t
Buffer tag type
#include <udi.h>typedef udi_ubit32_t udi_tagtype_t; /* Tag Category Masks */#define UDI_BUFTAG_ALL 0xffffffff #define UDI_BUFTAG_VALUES 0x000000ff #define UDI_BUFTAG_UPDATES 0x0000ff00 #define UDI_BUFTAG_STATUS 0x00ff0000 #define UDI_BUFTAG_DRIVERS 0xff000000 #define /* Value Category Tag Types */ #define UDI_BUFTAG_BE16_CHECKSUM (1U<<0) #define /* Update Category Tag Types */ #define UDI_BUFTAG_SET_iBE16_CHECKSUM (1U<<8) #define UDI_BUFTAG_SET_TCP_CHECKSUM (1U<<9) #define UDI_BUFTAG_SET_UDP_CHECKSUM (1U<<10) #define /* Status Category Tag Types */ #define UDI_BUFTAG_TCP_CKSUM_GOOD (1U<<17) #define UDI_BUFTAG_UDP_CKSUM_GOOD (1U<<18) #define UDI_BUFTAG_IP_CKSUM_GOOD (1U<<19) #define UDI_BUFTAG_TCP_CKSUM_BAD (1U<<21) #define UDI_BUFTAG_UDP_CKSUM_BAD (1U<<22) #define UDI_BUFTAG_IP_CKSUM_BAD (1U<<23) #define /* Drivers Category Tag Types */ #define UDI_BUFTAG_DRIVER1 (1U<<24) #define UDI_BUFTAG_DRIVER2 (1U<<25) #define UDI_BUFTAG_DRIVER3 (1U<<26) #define UDI_BUFTAG_DRIVER4 (1U<<27) #define UDI_BUFTAG_DRIVER5 (1U<<28) #define UDI_BUFTAG_DRIVER6 (1U<<29) #define UDI_BUFTAG_DRIVER7 (1U<<30) #define UDI_BUFTAG_DRIVER8 (1U<<31)DESCRIPTION The udi_tagtype_t type definition specifies the tag type used to specify a bitmask of one or more tags. These tags are subdivided into categories according to the general meaning of the tag. Each category can be easily identified or selected by using the appropriate category mask defined above.
The value tags defined in the Values category are typically used to store a numeric value associated with the portion of the buffer that the tag applies to. Since buffer data is stored in raw form any value tag must indicate the endianness interpretation of the buffer data as part of the tag type where appropriate. The value associated with the tag itself is passed to/from environment service calls in the driver's endianness regardless of the endianness of the buffer data.
UDI_BUFTAG_BE16_CHECKSUM - This tag's value is a 16-bit checksum that has been computed for the tagged range of the buffer. The tag value is in the driver's endianness but the checksum is computed as if the buffer contents are in big-endian 16-bit format.
The checksum is calculated by treating the specified portion of the buffer as an array of udi_ubit16_t elements and computing the sum of all elements modulo 216. If the length of the buffer portion is odd the "missing" low order byte of the last array element is treated as zero.The update tags defined in the Updates category are used to request an update of the buffer based on a computation or scan of the associated portion of the buffer. The tag value for these tags usually represents a location in the buffer where the result of the computation or scan is to be written.
UDI_BUFTAG_SET_TCP_CHECKSUM - This tag is used to indicate that the associated portion of the buffer is a TCP/IP packet for which the TCP checksum is to be set before transmission, The associated buffer section includes the data and both the TCP and IP headers. The tag's value is ignored.
The TCP checksum is computed by taking the unsigned sum of 16-bit elements modulo 216, then applying a ones-complement; the following elements are included in this checksum: the TCP header and data areas, the IP source and destination addresses, the IP specified length, and the IP protocol byte (0 extended). The TCP checksum is written as a 16-bit big-endian value at bytes 16 and 17 of the TCP header.
More information regarding the TCP checksum algorithm may be obtained by consulting the following IETF RFCs:RFC 1071 "Computing the Internet checksum"; RFC 1141 "Incremental updating of the Internet checksum"; RFC 1624 "Computation of the Internet Checksum via Incremental Update"; and RFC 1936 "Implementing the Internet Checksum in Hardware".UDI_BUFTAG_SET_UDP_CHECKSUM - This tag is used to indicate that the associated portion of the buffer is a UDP/IP packet for which the UDP checksum is to be set before transmission. The associated buffer section includes the data and both the UDP and IP headers. The tag's value is ignored.
The UDP checksum is the ones-complement of a 16-bit big-endian checksum of: the UDP header and data areas, the IP source and destination addresses, an additional copy of the UDP specified length, and the IP protocol byte (0 extended). The UDP checksum is written as a 16-bit big-endian value at bytes 6 and 7 of the UDP header.
More information regarding the UDP checksum algorithm may be obtained by consulting the IETF RFCs described above for the UDI_BUFTAG_SET_TCP_CHECKSUM buffer tag.UDI_BUFTAG_SET_iBE16_CHECKSUM - This tag is used to indicate that a 16-bit big-endian ones-complement checksum is to be generated for the tagged portion of the buffer and that the result must be written into the buffer at the offset specified by the tag's value field before transmitting the buffer.
This buffer tag is commonly used to request that the IP header checksum is to be set before transmitting the buffer.The status tags defined in the Status category are used to indicated the status of the associated portion of the buffer.
UDI_BUFTAG_TCP_CKSUM_GOOD - This tag is used to indicate that the associated portion of the buffer contains a TCP header and data portion and that the checksum contained in the header has been validated as correct for that buffer. This tag is typically set by a Network Adapter whose hardware validates TCP checksums for received packets. The checksum value itself, if known, may be specified as the tag_value for this tag; the header may no longer contain the checksum and this value in the packet header should not be reference.
UDI_BUFTAG_UDP_CKSUM_GOOD - This tag is used to indicate that the associated portion of the buffer contains a UDP header and data portion and that the checksum contained in the header has been validated as correct for that buffer. This tag is typically set by a Network Adapter whose hardware validates UDP checksums for received packets. The checksum value itself, if known, may be specified as the tag_value for this tag; the header may no longer contain the checksum and this value in the packet header should not be reference.
UDI_BUFTAG_IP_CKSUM_GOOD - This tag is used to indicate that the associated portion of the buffer contains an IP header (including options) and that the checksum contained in the header has been validated as correct for that buffer. This tag is typically set by a Network Adapter whose hardware validates IP checksums for received packets. The checksum value itself, if known, may be specified as the tag_value for this tag; the header may no longer contain the checksum and this value in the packet header should not be reference.
UDI_BUFTAG_TCP_CKSUM_BAD - This tag is used to indicate that the associated portion of the buffer contains a TCP header and data portion and that the checksum contained in the header does not match the calculated checksum (as typically determined by the driver or the hardware).
UDI_BUFTAG_UDP_CKSUM_BAD - This tag is used to indicate that the associated portion of the buffer contains a UDP header and data portion and that the checksum contained in the header does not match the calculated checksum (as typically determined by the driver or the hardware).
UDI_BUFTAG_IP_CKSUM_BAD - This tag is used to indicate that the associated portion of the buffer contains an IP header (including options) and that the checksum contained in the header does not match the calculated checksum.
The driver tags defined in the Drivers category are available for use by the driver for temporary or driver-internal use. This is especially useful when passing buffers in a multi-region driver. These tags are not visible to any other drivers; this protects against inter-driver confusion or tag assumptions but also means that these tags are not suitable for passing buffer information to other drivers in the UDI environment. Driver tags attached to a buffer which is passed to other drivers and subsequently returned will still have the current driver's tags attached and visible unless the associated region of the buffer was modified before being returned to the current driver; driver-specific tags set by other drivers will have no effect on the driver-specific tags set by the current driver.
NAME udi_buf_tag_t
Buffer tag structure
#include <udi.h>typedef struct { udi_tagtype_t tag_type; udi_ubit32_t tag_value; udi_size_t tag_off; udi_size_t tag_len; } udi_buf_tag_t;MEMBERS tag_type is the type of tag represented by this tag structure. Although udi_tagtype_t is a bitmask type only one tag type may be specified in the udi_buf_tag_t structure (i.e. only one bit may be set).
tag_value is the value associated with this tag.
tag_off is the starting buffer data offset for which the tag applies.
tag_len is the length of data (in bytes) for which the tag applies. The tag_len value must not be zero.
DESCRIPTION The udi_buf_tag_t structure is used to describe a buffer tag. The range of data to which the tag applies is specified by the tag_off and tag_len fields; the tag_type specifies which type of tag is being described. The tag_value is the associated value for this tag (if any) as defined by the tag_type.
REFERENCES udi_buf_tag_set, udi_buf_tag_get
NAME udi_buf_tag_set
Sets a tag for a portion of buffer data
#include <udi.h>void udi_buf_tag_set ( udi_buf_tag_set_call_t *callback, udi_cb_t *gcb, udi_buf_t *buf, udi_buf_tag *tag_array, udi_ubit16_t tag_array_length ); typedef void udi_buf_tag_set_call_t ( udi_cb_t *gcb, udi_buf_t *new_buf );ARGUMENTS callback, gcb are standard arguments described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions".
buf is the buffer for which the new tag is to be set.
tag_array is a pointer to an array of udi_buf_tag_t structures that are to be set in the current buffer.
tag_array_length is the number of entries in the tag_array.
new_buf is a pointer to the buffer with the new tag value set.
DESCRIPTION The udi_buf_tag_set operation is used to set one or more tags for the associated buffer. The tags to be set are specified in the tag_array and each tag will be set individually. If a tag in the input array is not a driver-specific tag and matches an existing buffer tag of the same type, offset, and length, the tag_value from the input array replaces the current tag value and the tag is otherwise unchanged. If no exactly matching type, offset, and length tag already exists for the buffer, a new tag will be created from the information in the array element.
The range specified by the tag offset and length must consist entirely of valid data.
WARNINGS Control block usage must follow the rules described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions".
On successful completion, buf will no longer be valid and new_buf must be used instead.
REFERENCES udi_buf_tag_t, udi_buf_tag_get
NAME udi_buf_tag_get
Gets one or more tags from a buffer
#include <udi.h>udi_ubit16_t udi_buf_tag_get ( udi_buf_t *buf, udi_tagtype_t tag_type, udi_buf_tag_t *tag_array, udi_ubit16_t tag_array_length, udi_ubit16_t tag_start_idx );ARGUMENTS buf is the buffer for which the tag information is to be returned
tag_type is a bitmask of tag types; only tags which correspond to bits set in this bitmask will be returned. For convenience, the tag category mask values may be used for this argument.
tag_array is a pointer to an array of udi_buf_tag_t structures that are to be filled in with the obtained tag information.
tag_array_length is the number of entries that may be written to tag_array.
tag_start_idx is the number of tags of the specified type to skip before returning tag information.
DESCRIPTION The udi_buf_tag_get operation is used to obtain information about tags which are attached to the buffer. Any available tags matching of of the requested tag_type bit values will be written into the tag_array (after skipping the first tag_start_idx tags) until either all tags of the target types or tag_array_length number of tags have been written.
return values This function returns the actual number of tags of the selected types, regardless of the input tag_start_idx. The tag_start_idx may be used to iterate through all tags if tag_array_length is less than the number of defined tags.
REFERENCES udi_buf_tag_t, udi_buf_tag_set
13.7.2 Buffer Tag Utilities
This section defines a set of utility routines that may be used to efficiently make use of buffer tags. The functionality provided by these utility routines could alternatively be implemented by discrete operations using udi_buf_tag_get and udi_buf_tag_set and other buffer management service calls. These utility routines are provided to assist in implementing and supporting the most common set of buffer tag operations, such as calculating network data checksums.
NAME udi_buf_tag_compute
Compute values from tagged buffer data
#include <udi.h>udi_ubit32_t udi_buf_tag_compute ( udi_buf_t *buf, udi_size_t off, udi_size_t len, udi_tagtype_t tag_type );ARGUMENTS buf is the buffer for which the tag value is to be computed.
off is the offset into the buffer at which the computation is to begin. The offset specified must point to valid buffer data.
len is the number of bytes in the buffer to be used for the computation. All bytes in the buffer specified by off and len must be valid buffer data.
tag_type is the tag value to be computed. Only one tag type may be specified (only one bit may be set for this argument) and it must be one of the Value category tags (i.e. one of the tag types in the UDI_BUFTAG_VALUES category).
DESCRIPTION The udi_buf_tag_compute utility routine is used to calculate the specified tag value for a portion of data contained in the buffer; the most common tag value computed is the 16-bit big-endian checksum value used for network packets.
The buffer range specified must consist entirely of valid data bytes.
The tag_type argument specifies what type of tag value is to be calculated. It is assumed (but not required) that this utility will take advantage of existing tags attached to the buffer to optimize the computation of the tag values.
This utility function does not actually set a tag of the corresponding tag_type on the buffer itself; that activity is left to the caller if needed.
Note - This function could be implemented entirely as a series of calls to various UDI service calls such as udi_buf_read and udi_buf_tag_get, but is expected in most environments to be implemented more directly in terms of the underlying implementation-specific data structures for greater efficiency.
RETURN VALUE The computed tag value.
REFERENCES udi_buf_tag_apply, udi_buf_tag_get
NAME udi_buf_tag_apply
Apply modifications to tagged buffer data
#include <udi.h>void udi_buf_tag_apply ( udi_buf_tag_apply_call_t *callback, udi_cb_t *gcb, udi_buf_t *buf, udi_tagtype_t tag_type ); typedef void udi_buf_tag_apply_call_t ( udi_cb_t *gcb, udi_buf_t *new_buf );ARGUMENTS callback, gcb are standard arguments described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions".
buf is the buffer for which tag values are to be computed and set.
tag_type is a bitmask of tag types for which the tag values are to be set. Only bit values corresponding to the UDI_BUFTAG_UPDATES mask may be used; for convenience the mask value itself may be specified.
new_buf is the buffer returned to the caller after tags have been computed and have been written into the buffer.
DESCRIPTION The udi_buf_tag_apply utility routine is used to process any Update category tags in the buffer. These buffer tags specify various tag values that are to be generated and inserted into the buffer as part of the handling of that buffer (e.g. for TCP/IP network checksum generation before transmitting the buffer).
This utility will process all tags attached to the buffer which correspond to bits set in the specified tag_type. For each tag it will compute the tag value for the indicated section of the buffer (as if by a call to udi_buf_tag_compute) and then write the result into the buffer according to the description of that tag_type. The requested update tags will not be processed in any particular order; if a specific order of computation is desired multiple calls to udi_buf_tag_apply should be made with the required sequence of tag types.
This utility function is typically used by Network Interface Card (NIC) Drivers which do not provide a checksum off-load capability and need to insert various TCP or other protocol-specific checksums into the packet before it is transmitted.
Note - This function could be implemented entirely as a series of calls to various UDI service calls such as udi_buf_write and udi_buf_tag_get, but is expected in most environments to be implemented more directly in terms of the underlying implementation-specific data structures for greater efficiency.
WARNINGS Control block usage must follow the rules described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions".
On successful completion, buf will no longer be valid and new_buf is substituted, even if buf was not specified as NULL; new_buf may return the same handle value as the input value of buf.
If this operation is cancelled with udi_cancel, any pre-existing buf buffer will be discarded (see udi_cancel for an explanation of why this is so).
REFERENCES udi_buf_tag_t, udi_buf_tag_get, udi_buf_tag_compute