I/O messages

An I/O message is one that relies on an existing binding (e.g. OCB) between the client and the resource manager.

As an example, an _IO_READ (from the client's read() function) message depends on the client's having previously established an association (or context) with the resource manager by issuing an open() and getting back a file descriptor. This context, created by the open() call, is then used to process the subsequent I/O messages, like the _IO_READ.

There are good reasons for this design. It would be inefficient to pass the full pathname for each and every read() request, for example. The open() handler can also perform tasks that we want done only once (e.g. permission checks), rather than with each I/O message. Also, when the read() has read 4096 bytes from a disk file, there may be another 20 megabytes still waiting to be read. Therefore, the read() function would need to have some context information telling it the position within the file it's reading from, how much has been read, and so on.

The resmgr_io_funcs_t structure (which you pass to resmgr_attach() along with the connect functions) defines the functions to call for the I/O messages. The resmgr_io_funcs_t structure is defined in <sys/resmgr.h> as follows:

typedef struct _resmgr_io_funcs {
    unsigned    nfuncs;
    int (*read)       (resmgr_context_t *ctp, io_read_t *msg,
                       RESMGR_OCB_T *ocb);
    int (*write)      (resmgr_context_t *ctp, io_write_t *msg,
                       RESMGR_OCB_T *ocb);
    int (*close_ocb)  (resmgr_context_t *ctp, void *reserved,
                       RESMGR_OCB_T *ocb);
    int (*stat)       (resmgr_context_t *ctp, io_stat_t *msg,
                       RESMGR_OCB_T *ocb);
    int (*notify)     (resmgr_context_t *ctp, io_notify_t *msg,
                       RESMGR_OCB_T *ocb);
    int (*devctl)     (resmgr_context_t *ctp, io_devctl_t *msg,
                       RESMGR_OCB_T *ocb);
    int (*unblock)    (resmgr_context_t *ctp, io_pulse_t *msg,
                       RESMGR_OCB_T *ocb);
    int (*pathconf)   (resmgr_context_t *ctp, io_pathconf_t *msg,
                       RESMGR_OCB_T *ocb);
    int (*lseek)      (resmgr_context_t *ctp, io_lseek_t *msg,
                       RESMGR_OCB_T *ocb);
    int (*chmod)      (resmgr_context_t *ctp, io_chmod_t *msg,
                       RESMGR_OCB_T *ocb);
    int (*chown)      (resmgr_context_t *ctp, io_chown_t *msg,
                       RESMGR_OCB_T *ocb);
    int (*utime)      (resmgr_context_t *ctp, io_utime_t *msg,
                       RESMGR_OCB_T *ocb);
    int (*openfd)     (resmgr_context_t *ctp, io_openfd_t *msg,
                       RESMGR_OCB_T *ocb);
    int (*fdinfo)     (resmgr_context_t *ctp, io_fdinfo_t *msg,
                       RESMGR_OCB_T *ocb);
    int (*lock)       (resmgr_context_t *ctp, io_lock_t *msg,
                       RESMGR_OCB_T *ocb);
    int (*space)      (resmgr_context_t *ctp, io_space_t *msg,
                       RESMGR_OCB_T *ocb);
    int (*shutdown)   (resmgr_context_t *ctp, io_shutdown_t *msg,
                       RESMGR_OCB_T *ocb);
    int (*mmap)       (resmgr_context_t *ctp, io_mmap_t *msg,
                       RESMGR_OCB_T *ocb);
    int (*msg)        (resmgr_context_t *ctp, io_msg_t *msg,
                       RESMGR_OCB_T *ocb);
    int (*reserved)   (resmgr_context_t *ctp, void *msg,
                       RESMGR_OCB_T *ocb);
    int (*dup)        (resmgr_context_t *ctp, io_dup_t *msg,
                       RESMGR_OCB_T *ocb);
    int (*close_dup)  (resmgr_context_t *ctp, io_close_t *msg,
                       RESMGR_OCB_T *ocb);
    int (*lock_ocb)   (resmgr_context_t *ctp, void *reserved,
                       RESMGR_OCB_T *ocb);
    int (*unlock_ocb) (resmgr_context_t *ctp, void *reserved,
                       RESMGR_OCB_T *ocb);
    int (*sync)       (resmgr_context_t *ctp, io_sync_t *msg,
                       RESMGR_OCB_T *ocb);
    int (*power)      (resmgr_context_t *ctp, io_power_t *msg,
                       RESMGR_OCB_T *ocb);
} resmgr_io_funcs_t;

You initialize this structure in the same way as the resmgr_connect_funcs_t structure: call iofunc_func_init() to fill it with pointers to the default handlers, and then override any that your resource manager needs to handle specifically. This structure also begins with an nfuncs member that indicates how many functions are in the structure, to allow for future expansion.

Note: The resmgr_attach() function copies the pointers to the resmgr_connect_funcs_t and resmgr_io_funcs_t structures, not the structures themselves. You should allocate the structures, declare them to be static, or make them global variables. If your resource manager is for more than one device with different handlers, create separate structures that define the handlers.

Notice that the I/O functions all have a common parameter list. The first entry is a resource manager context structure, the second is a message (the type of which matches the message being handled and contains parameters sent from the client), and the last is an OCB (containing what we bound when we handled the client's open() function).

You usually have to provide a handler for the following entries:

read
Handles client calls to read() and readdir(). The message type is _IO_READ. For more information about the io_read handler, see "Handling the _IO_READ message" in the Handling Read and Write Messages chapter.
write
Handles client calls to write(), fwrite(), and so on. The message type is _IO_WRITE. For more information about the io_write handler, see "Handling the _IO_WRITE message" in the Handling Read and Write Messages chapter.
devctl
Handles client calls to devctl() and ioctl(). The message type is _IO_DEVCTL. For more information about the io_devctl handler, see "Handling devctl() messages" in the Handling Other Messages chapter.

You typically use the default entry for the following:

close_ocb
Called by the library when the last close() has been received by a particular OCB. You can use this handler to clean up anything associated with the OCB.
stat
Handles client calls to stat(), lstat(), and fstat(). The message type is _IO_STAT. For more information about the io_stat handler, see "Handling stat()" in the Handling Other Messages chapter.
notify
Handles client calls to select() and ionotify(). The message type is _IO_NOTIFY. For more information about the io_notify handler, see "Handling ionotify() and select()" in the Handling Other Messages chapter.
unblock
Handles requests from the kernel to unblock the client during the I/O message phase. There's no message associated with this. For more information about the io_unblock handler, see "Handling client unblocking due to signals or timeouts" in the Signals, Timeouts, and Interrupts chapter.
pathconf
Handles client calls to fpathconf() and pathconf(). The message type is _IO_PATHCONF.
lseek
Handles client calls to lseek(), fseek(), and rewinddir(). The message type is _IO_LSEEK. For more information about the io_lseek handler, see "Handling lseek()" in the Handling Other Messages chapter.
chmod
Handles client calls to chmod() and fchmod(). The message type is _IO_CHMOD.
chown
Handles client calls to chown() and fchown(). The message type is _IO_CHOWN.
utime
Handles client calls to utime(). The message type is _IO_UTIME.
openfd
Handles client calls to openfd(). The message type is _IO_OPENFD.
fdinfo
Handles client calls to iofdinfo(). The message type is _IO_FDINFO.
lock
Handles client calls to fcntl(), lockf(), and flock(). The message type is _IO_LOCK.
space
Handles client calls to chsize(), fcntl(), ftruncate(), and ltrunc(). The message type is _IO_SPACE.
shutdown
Reserved for future use.
mmap
Handles client calls to mmap(), munmap(), mmap_device_io(), and mmap_device_memory(). The message type is _IO_MMAP.
msg
Handles messages that are manually assembled and sent via MsgSend(). The message type is _IO_MSG. For more information about the io_msg handler, see "Handling out-of-band (_IO_MSG) messages" in the Handling Other Messages chapter.
reserved
Reserved for future use.
dup
Handles client calls to dup(), dup2(), fcntl(), fork(), spawn*(), and vfork(). The message type is _IO_DUP. For more information about the io_dup handler, see "Handling open(), dup(), and close() messages" in the Handling Other Messages chapter.
close_dup
Handles client calls to close() and fclose(). The message type is _IO_CLOSE_DUP.
Note: You'll almost never replace the default close_dup handler because the library keeps track of multiple open(), dup(), and close() calls for an OCB. For more information, see "open(), dup(), and close()," below.
lock_ocb
Locks the attributes structure pointed to by the OCB. This is done to ensure that only one thread at a time is operating on both the OCB and the corresponding attributes structure. The lock (and corresponding unlock) functions are synthesized by the resource manager library before and after completion of message handling.
unlock_ocb
Unlocks the attributes structure pointed to by the OCB.
sync
Handles client calls to fsync() and fdatasync(). The message type is _IO_SYNC.
power
Reserved for future use.