DOC HOME SITE MAP MAN PAGES GNU INFO SEARCH PRINT BOOK
 
TOC PREV NEXT INDEX

Control Block Management

11

11.1 Overview

The UDI service calls available to the driver can be divided into two classes:

  1. service calls not requiring external resources
  2. service calls that (may) require external resources

Service calls of the first type resemble conventional system service calls, however service calls of the second type may require the environment to obtain resources to complete the service request. When resources must be obtained, the service call cannot complete immediately with the requested resources because they may not be presently available; a callback is used instead to handle completion for these types of requests so that the driver may be re-entered once the resources are available.

Service calls of the first type are referred to as synchronous service calls, whereas those of the second type are referred to as asynchronous service calls. See also the discussion of "Asynchronous Service Calls" and the "Function Call Classifications".

The UDI control block provides the context for the second type of service call. The control block can be used to marshall and unmarshall the parameters for a request and to allow the environment to queue the request internally and maintain context-oriented status. While the driver owns the control block it can be used for similar queuing and status/context purposes within the driver.

Metalanguage-specific channel operations (see Chapter 23, "Introduction to UDI Metalanguages") also use UDI control blocks for similar purposes.

The generic control block is a representation of the basic elements common to all UDI control blocks. Most UDI service calls requiring a control block will accept any control block but are defined in terms of the generic control block; convenience macros are also provided to obtain a generic control block reference for any specific control block and vice versa.

11.2 Control Block Service Calls and Macros

The service calls and macros used to manipulate control blocks are described in the paragraphs that follow.

NAME udi_cb_t

Generic, least-common-denominator control block

SYNOPSIS

#include <udi.h>

typedef struct {

	udi_channel_t channel;

	void *context;

	void *scratch;

	void *initiator_context;

	udi_origin_t origin;

} udi_cb_t;
 

MEMBERS channel is a handle to the channel currently associated with this control block. When used in a channel operation, the main control block's channel member is used as the target channel, and must not-at that time-be UDI_NULL_CHANNEL. For environment implementation reasons, channel must never be explicitly set by the driver to UDI_NULL_CHANNEL.

context is a pointer to state information within the driver region. On entry to a channel operation, the environment sets context to the channel's current context. Drivers may change it if needed.

See udi_channel_set_context for details on how channel context is determined.

scratch is a pointer to the control block's scratch area. Drivers must not change this pointer, but may change any of the bytes in the space pointed to by scratch, up to the required scratch size specified by the appropriate udi_cb_init_t in the driver's udi_init_info.

initiator_context is a context pointer that the initiator of a request or indication operation can use to associate per-request context with this control block. If and when the control block is returned to the initiator via an acknowledgement, nak, or response operation, the initiator can use this context pointer to access any additional state it needs to complete the operation.

Any driver receiving a request or indication operation must use the same control block in its (acknowledgement, nak, or response) reply, and must not modify the initiator_context value. In fact, the value of initiator_context is unspecified except when the control block is owned by the initiating region, so must not be compared, dereferenced, or otherwise used from any other region.

origin is a handle to the origination information for the current request. This is set in the original control block by the environment; each module must copy this field from input control blocks to any other control blocks used to complete work requested by the input control block. Any control block used in an asynchronous service call or channel operation that is not associated with an incoming request control block must set origin to the UDI_NULL_ORIGIN value.

DESCRIPTION The udi_cb_t structure is used for generically handling control blocks and accessing their common members. All metalanguage-specific control blocks have a udi_cb_t structure as their first structure member.

The udi_cb_t structure is a semi-opaque type, and must only be allocated by environment service calls. Control blocks are transferable between regions, when used as the main control block for a channel operation, or chained from that control block as part of a linked list of identically-typed control blocks.

When a new control block is allocated, its context and origin members are initialized to the context value from the original control block, its channel member is initialized according to the default_channel argument block passed to udi_cb_alloc, and its initiator_context value is unspecified.

The driver that currently owns the control block may change the channel and context members at any time while the control block is not in use with an environment service call. If the control block is not already part of an in-progress request/response sequence (that is, not transferred to this region from another region as part of a request or indication operation), the controlling driver may also change the initiator_context value.

All members of udi_cb_t and other visible fields in a metalanguage-specific control block, as well as the scratch area contents, are preserved across asynchronous service calls, but not across channel operations. The only member of udi_cb_t that is preserved across a channel operation is initiator_context, and that only when the control block is returned to the initiating region.

When a control block or a chain of control blocks is passed to another region via a channel operation, the channel and context members of each control block are automatically set to the channel handle for the target region's end of the channel and the channel context for that endpoint, respectively, before the target region's entry point is invoked.

references udi_init_info, udi_cb_init_t, udi_cb_alloc

NAME udi_cb_alloc

Allocate a new control block

SYNOPSIS

#include <udi.h>

void udi_cb_alloc (

	udi_cb_alloc_call_t *callback,

	udi_cb_t *gcb,

	udi_index_t cb_idx,

	udi_channel_t default_channel );
 
typedef void udi_cb_alloc_call_t (

	udi_cb_t *gcb,

	udi_cb_t *new_cb );
 

ARGUMENTS callback, gcb are standard arguments described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions".

cb_idx is a control block index that indicates required properties of the control block, such as metalanguage type and scratch size.

default_channel is a channel handle that, if set to a value other than UDI_NULL_CHANNEL, is used as the initial value for the new control block's channel member. If set to UDI_NULL_CHANNEL, the environment is free to initialize the channel member with some other value, so the driver must not depend on it containing UDI_NULL_CHANNEL.

new_cb is a pointer to the newly allocated control block.

DESCRIPTION udi_cb_alloc allocates a new control block for use by the driver. The new control block can be used to allocate other resources using any UDI service request or to invoke channel operations appropriate to the specified control block type.

While such allocations are usually performed using a specific control block already associated with a channel operation, the new control block returned by udi_cb_alloc provides a way to continue or complete the channel operation without waiting for a service call to complete. This is particularly useful when initiating delayed callbacks with udi_timer_start or udi_timer_start_repeating.

When a new control block is allocated, its context member is initialized to the context value from gcb, its origin member is initialized to the origin value from gcb, its channel member is initialized according to the default_channel argument block passed to udi_cb_alloc, and its initiator_context value is unspecified.

The scratch pointer of the new control block is initialized to point to the associated scratch area and the pointer must not be modified by the driver. If the driver's scratch requirement is zero, the value of the scratch pointer is unspecified and it must not be dereferenced.

The initial values in the new control block's scratch space are unspecified; they are not guaranteed to be zero. Similarly, for metalanguage-specific control blocks that have additional visible structure members, the initial value of these structure members are also unspecified.

WARNINGS The control block obtained with this call must not be used with metalanguage-related channel operations other than those appropriate for the control block type associated with cb_idx. If the control block index was associated with a udi_gcb_init_t in udi_init_info, rather than a metalanguage-specific udi_cb_init_t, then the new control block must not be used with any channel operations.

The driver must not explicitly set the channel member of the returned control block to UDI_NULL_CHANNEL at any time and must not expect UDI_HANDLE_IS_NULL to return TRUE for the channel member of a control block even if default_channel was UDI_NULL_CHANNEL.

Control block usage must follow the rules described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions".

REFERENCES udi_cb_t, udi_cb_free, udi_timer_start, udi_timer_start_repeating, udi_init_info, udi_cb_init_t

NAME udi_cb_alloc_dynamic

Allocate a control block with variable inline layout

SYNOPSIS

#include <udi.h>

void udi_cb_alloc_dynamic (

	udi_cb_alloc_call_t *callback,

	udi_cb_t *gcb,

	udi_index_t cb_idx,

	udi_channel_t default_channel,

	udi_size_t inline_size,

	udi_layout_t *inline_layout );
 
typedef void udi_cb_alloc_call_t (

	udi_cb_t *gcb,

	udi_cb_t *new_cb );
 

ARGUMENTS callback, gcb are standard arguments described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions".

cb_idx are the same arguments as used in udi_cb_alloc.

default_channel

new_cb

inline_size is the size of the previously unspecified inline structure for this cb_idx.

inline_layout is the layout of the previously unspecified inline structure for this cb_idx. Must be NULL if the control block layout does not include UDI_DL_INLINE_DRIVER_TYPED.

DESCRIPTION udi_cb_alloc_dynamic behaves like udi_cb_alloc, except that it allows the driver to specify the size and layout of an inline structure for the control block that was left unspecified in the driver's udi_cb_init_t structure with the given cb_idx.

The inline_size and inline_layout members of the corresponding udi_cb_init_t structure (see page 10-11) must have been set to zero and NULL, respectively, and the control block layout must include exactly one UDI_DL_INLINE_UNTYPED, UDI_DL_INLINE_TYPED, or UDI_DL_INLINE_DRIVER_TYPED layout element.

It is recommended that udi_cb_alloc be used instead of udi_cb_alloc_dynamic if possible, as it's likely to be faster, but if the layout is not known statically, udi_cb_alloc_dynamic must be used.

warnings Use of the inline_layout parameter must conform to the rules described in Section 5.2.1.1, "Using Memory Pointers with Asynchronous Service Calls".

REFERENCES udi_cb_t, udi_cb_alloc, udi_layout_t, udi_init_info, udi_cb_init_t

NAME udi_cb_alloc_batch

Allocate a batch of control blocks with buffers

SYNOPSIS

#include <udi.h>

void udi_cb_alloc_batch (

	udi_cb_alloc_batch_call_t *callback,

	udi_cb_t *gcb,

	udi_index_t cb_idx,

	udi_index_t count,

	udi_boolean_t with_buf,

	udi_size_t buf_size,

	udi_buf_path_t path_handle );
 
typedef void udi_cb_alloc_batch_call_t (

	udi_cb_t *gcb,

	udi_cb_t *first_new_cb );
 

ARGUMENTS callback, gcb are standard arguments described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions".

cb_idx is a control block index that indicates required properties of the control block, such as metalanguage type and scratch size. All of the control blocks allocated will be of the same type as indicated by this cb_idx.

count is the number of control blocks of this type to allocate in the batch operation.

with_buf is true if buffers should be allocated along with the control blocks. If true, a buffer of size buf_size will be allocated for each udi_buf_t pointer (UDI_DL_BUF layout entry) that exists in each allocated control block. If false, no buffers will be allocated and the values of the corresponding control block buffer pointers are undefined.

buf_size is the size of the buffers to be allocated if with_buf is true. This argument is ignored if with_buf is false.

path_handle is the handle identifying the intended use and dispatching of the allocated buffers. 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. This field is ignored if with_buf is false.

first_new_cb is a pointer to the first allocated control block in the list of returned control blocks. If count is zero, first_new_cb will be NULL.

DESCRIPTION This service combines the use of udi_cb_alloc with UDI_BUF_ALLOC to allocate batches of one or more control blocks with optional associated buffers.

Consult udi_cb_alloc for more specifics on how the individual control blocks will be allocated and initialized.

Consult UDI_BUF_ALLOC and udi_buf_write for more specifics on how the individual buffers will be allocated and initialized.

The control blocks are returned to the caller by passing them as a chain or list. If the control block type allows control block chaining (i.e. the control block contains a pointer to another control block of the same type) then the chain field within the control blocks are used to link the returned control blocks: each control block's chain field will point to the next control block in the chain. If the control block type does not support chaining, then the initiator_context field of the returned control blocks is used to link the control blocks; the callback function should unlink the control blocks and reset the initiator_context as appropriate. The link pointer in the last control block shall be set to NULL.

WARNINGS See the warnings for udi_cb_alloc.

Batch allocated control blocks must be unlinked before use unless actually used as a chain. Passing a control block to a channel operation or system service call relenquishes ownership of that control block and any chained control blocks. If the list is maintained via the initiator_context, the driver is assured that the initiator_context will be returned unchanged, but is not guaranteed that the initiator_context will not be changed (or deallocated) while the driver does not own the control block.

REFERENCES udi_cb_t, udi_cb_alloc, udi_buf_t, UDI_BUF_ALLOC, udi_buf_write

NAME udi_cb_free

Deallocates a previously obtained control block

SYNOPSIS

#include <udi.h>

void udi_cb_free ( udi_cb_t *cb );
 

ARGUMENTS cb is a pointer to the control block to be deallocated. If NULL, this function is a no-op.

DESCRIPTION udi_cb_free releases the specified control block, including any metalanguage-specific parts, along with any associated resources back to the environment. cb must be NULL or must have been previously obtained by a call to udi_cb_alloc, or passed to the driver via a channel operation.

Note that udi_cb_free may be used to free any type of control block.

WARNING The control block must not currently have any service call or callback pending. Any pending requests must first be cancelled with udi_cancel.

Management metalanguage control blocks and channel event control blocks must not be passed to udi_cb_free.

Control block usage must follow the rules described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions"

REFERENCES udi_cb_alloc, udi_cancel, udi_channel_event_cb_t

NAME UDI_GCB

Convert any control block to generic udi_cb_t

SYNOPSIS

#include <udi.h>

#define UDI_GCB(mcb) (&(mcb)->gcb)
 

ARGUMENTS mcb is a pointer to the control block reference to be converted.

DESCRIPTION This macro is used to convert any UDI control block pointer into its generic control block representation (udi_cb_t *) suitable for use with a UDI service request. The original control block is not copied or re-allocated.

This macro is provided for convenience only. Its use is highly recommended but not required.

REFERENCES udi_cb_t

NAME UDI_MCB

Convert a generic control block to a specific one

SYNOPSIS

#include <udi.h>

#define UDI_MCB(gcb, cb_type) ((cb_type *)(gcb))
 

ARGUMENTS gcb is a pointer to the control block reference to be converted.

cb_type is the type name for the desired specific control block type.

DESCRIPTION This macro is used to convert a generic control block pointer to a metalanguage-specific control block type. The original control block is not copied or re-allocated. The control block itself must already be of the type appropriate to cb_type.

This macro is provided for convenience only. Its use is highly recommended but not required.

WARNINGS The control block referenced by gcb must have been previously obtained by a call to udi_cb_alloc with a cb_idx appropriate to cb_type.

Control block usage must follow the rules described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions"

REFERENCES udi_cb_t

NAME udi_cancel

Cancel a pending asynchronous service call

SYNOPSIS

#include <udi.h>

void udi_cancel (

	udi_cancel_call_t *callback,

	udi_cb_t *gcb );
 
typedef void udi_cancel_call_t (

	udi_cb_t *gcb );
 

ARGUMENTS callback, gcb are standard arguments described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions".

DESCRIPTION Any service request with a pending callback can be canceled by this call, except timer requests (which must be canceled with udi_timer_cancel). The control block must be the same one specified when the service was requested, and must be active (i.e. the callback has not yet been called, regardless of whether or not allocations have actually completed).

udi_cancel must not be used with control blocks that have been passed to channel operations, but some channel operations can be aborted by using udi_channel_op_abort.

When a service request is cancelled, any whole or partially-allocated resources or data structures that would have been returned with that callback upon normal completion will be discarded (i.e. there will be no resource leaks). Further, any resources or data structures that would have been consumed by the original request (e.g. movable structs and objects referenced by transferable handles) will be consumed (and discarded), since there is no way to pass the object back to the original caller. Another way to look at this is that udi_cancel does not provide an undo operation, but rather an abort operation; any objects (such as a data buffer for udi_buf_write) being modified or created by the original request are destroyed by the abort.

Once the request has been cancelled, and any partial allocations released, the specified callback routine will be called instead of the original callback routine from the outstanding request. Ownership of the control block is transferred back to the requestor with this callback, and the control block is available for reuse.

WARNINGS udi_cancel must be called from the region that owned the control block at the time of the original request. It cannot be used to cancel a pending request in another region.

A driver must keep track of its in-progress requests to avoid canceling a different request than intended. See the example below for details. A good rule of thumb is that udi_cancel must not be used to cancel a request without first checking to see if the corresponding callback has been called.

If a driver issues a udi_cancel for a control block that is not active the driver is in error. See the "Driver Faults/Recovery" section of "Execution Model" for an explanation of how the environment may react to this driver error.

Control block usage must follow the rules described in the "Asynchronous Service Calls" section of "Calling Sequence and Naming Conventions"

Since ownership of control blocks are transferred away from the driver upon issuing a channel operation, any attempt to use udi_cancel to cancel a channel operation will be considered an error and will be handled as an environment-detected error in accordance with the "Driver Faults/Recovery" section of "Execution Model".

EXAMPLES The first example shows how not to use udi_cancel. The udi_cancel call in ddd_step1 will not necessarily cancel the udi_mem_alloc call that immediately precedes it. In fact, it could even cancel the subsequent udi_cb_alloc request in ddd_step2, or even some further subsequent allocation in the callback sequence. This example is somewhat contrived in that there would typically be some reason the driver is canceling the request; it wouldn't simply do an allocation followed immediately by a cancel, but it illustrates the issues.

void

ddd_step1(ddd_context_t *context)

{

	...

	udi_mem_alloc(ddd_step2, UDI_GCB(cb1), size, 0);

	udi_cancel(ddd_step1a, UDI_GCB(cb1));

}
 
void

ddd_step1a(

	udi_cb_t *gcb) 

{

	/* Something has been canceled,

		but it's unclear what */

	...

}
 
void

ddd_step2(

	udi_cb_t *gcb,

	void *new_mem) 

{

	...

	udi_cb_alloc(ddd_step3, UDI_GCB(cb1), idx, chan);

}
 
void

ddd_step3(

	udi_cb_t *gcb,

	udi_cb_t *new_cb) 

{

	...

}
 

To fix this problem, the driver must first check to see if the corresponding allocation callback has been received before calling udi_cancel. Adding such a check to the above code produces the following, which will cancel the immediately preceding udi_mem_alloc call if and only if the allocation doesn't complete immediately (i.e. isn't complete upon return). (Note that some environments may be designed to never do the callback immediately before returning. So this would not in general be a useful thing to do in the driver, but it does illustrate the issues.)

void

ddd_step1(

	ddd_context_t *context) 

{

	...

	context->mem_alloc_done = FALSE;

	udi_mem_alloc(ddd_step2, UDI_GCB(cb1), size, 0);

	if (!context->mem_alloc_done) 

	    udi_cancel(ddd_step1a, UDI_GCB(cb1));

}
 
void

ddd_step1a(

	udi_cb_t *gcb) 

{

	/* udi_mem_alloc in step1 has been cancelled. */

	...

}
 
void

ddd_step2(

	udi_cb_t *gcb,

	void *new_mem) 

{

	ddd_context_t *context = gcb->context;

	...

	context->mem_alloc_done = TRUE; 

	udi_cb_alloc(ddd_step3, UDI_GCB(cb1), idx, chan);

}
 
void

ddd_step3(

	udi_cb_t *gcb,

	udi_cb_t *new_cb) 

{

	...

}
 

Note that the region serialization rules prevent reentrancy in the region code and therefore prevent the race conditions related to accesses and modifications of the mem_alloc_done variable that would normally need to be considered.


TOC PREV NEXT INDEX