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 shown below.
Note:
In order to correctly define RESMGR_OCB_T, #include
<sys/iofunc.h> before <sys/resmgr.h>.
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 Unblocking Clients and Handling 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.
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.