Extending the OCB

In some cases, you may find the need to extend the OCB. This is relatively painless to do. The common uses for extending the OCB are to add extra flags you wish to maintain on a per-open basis. One such flag could be used with the io_unblock() handler to cache the value of the kernel's _NTO_MI_UNBLOCK_REQ flag. (See the Message Passing chapter, under “Using the _NTO_MI_UNBLOCK_REQ” for more details.)

To extend the OCB, you'll need to provide two functions—one to allocate (and initialize) the new OCB and one to free it—to override the defaults, iofunc_ocb_calloc() and iofunc_ocb_free(). Then, you'll need to bind your two customized functions into the mount structure. (Yes, this does mean that you'll need a mount structure, if only for this one purpose.) Finally, you'll need to define your own OCB typedef, so that the prototypes for the code are all correct.

Let's look at the OCB typedef first, and then we'll see how to override the functions:

#define IOFUNC_OCB_T struct my_ocb
#include <sys/iofunc.h>

This tells the included file, <sys/iofunc.h>, that the manifest constant IOFUNC_OCB_T now points to your new and improved OCB structure.

Note: It's very important to keep in mind that the “normal” OCB must appear as the first entry in your extended OCB! This is because the POSIX helper library passes around a pointer to what it expects is a normal OCB—it doesn't know about your extended OCB, so therefore the first data element at the pointer location must be the normal OCB.

Here's our extended OCB:

typedef struct my_ocb
    iofunc_ocb_t    normal_ocb;
    int             my_extra_flags;
} my_ocb_t;

Finally, here's the code that illustrates how to override the allocation and deallocation functions in the mount structure:

// declare
iofunc_mount_t      mount;
iofunc_funcs_t      mount_funcs;

// set up the mount functions structure
// with our allocate/deallocate functions

// _IOFUNC_NFUNCS is from the .h file
mount_funcs.nfuncs = _IOFUNC_NFUNCS;

// your new OCB allocator
mount_funcs.ocb_calloc = my_ocb_calloc;

// your new OCB deallocator
mount_funcs.ocb_free = my_ocb_free;

// set up the mount structure
memset (&mount, 0, sizeof (mount));

Then all you have to do is bind the mount functions to the mount structure, and the mount structure to the attributes structure:


mount.funcs = &mount_funcs;
attr.mount = &mount;

The my_ocb_calloc() and my_ocb_free() functions are responsible for allocating and initializing an extended OCB and for freeing the OCB, respectively. They are prototyped as:

my_ocb_calloc (resmgr_context_t *ctp, IOFUNC_ATTR_T *attr);

my_ocb_free (IOFUNC_OCB_T *ocb);

This means that the my_ocb_calloc() function gets passed both the internal resource manager context and the attributes structure. The function is responsible for returning an initialized OCB. The my_ocb_free() function gets passed the OCB and is responsible for releasing the storage for it.

Note: It's important to realize that the OCB may be allocated by functions other than the normal io_open() handler—for example, the memory manager may allocate an OCB. The impact of this is that your OCB allocating function must be able to initialize the OCB with the attr argument.

There are two interesting uses for these two functions (that have nothing to do with extending the OCB):

Monitoring the allocation and deallocation of OCBs
In this case, you can simply “tie in” to the allocator/deallocator and monitor the usage of the OCBs (for example, you may wish to limit the total number of OCBs outstanding at any given time). This may prove to be a good idea if you're not taking over the io_open() outcall, and yet still need to intercept the creation of (and possibly deletion of) OCBs.
Providing more efficient allocation and deallocation
Another reason for overriding the library's built-in OCB allocator/deallocator is that you may wish to keep the OCBs on a free list, instead of using the library's calloc() and free() functions. If you're allocating and deallocating OCBs at a high rate, this may prove to be more efficient.