Friday May 13, 2005 Ever wondered, what would it take to write a minimum STREAMS module that can be correctly installed onto Solaris system? Here is an example of such module (which we will call nullmod) with some explanations.
Every STREAMS module should define the following entry points:
Since our module will only pass messages back and forth it will use putnext(9f)
as both write and read side put procedure and will not have any service
procedures. So we need to define only open/close functions which we will call nullmodopen() and nullmodclose().
/*
* Module open routine.
* Mark the module as "opened" and link it to
* the STRREAM.
*/
static int
nullmodopen(queue_t *rq, dev_t *dev,
int oflag, int sflag, cred_t *crp)
{
if (sflag != MODOPEN)
return (EINVAL);
/*
* Prevent duplicate opens.
* The q_ptr is reserved for module private use
*/
if (rq->q_ptr != NULL)
return (0);
/* Mark the module as "opened" */
rq->q_ptr = WR(rq)->q_ptr = (void *)1;
/* Link the module into the STREAM */
qprocson(rq);
/*
* At this point module is linked in the STREAM
* and can send/receive messages.
* Its put/service procedures may execute at any time.
*/
return (0);
}
/*
* Module close routine.
* Disconnect the module from the STREAM.
*/
static int
nullmodclose(queue_t *rq)
{
/* Disconnect the module from the STREAM */
qprocsoff(rq);
/*
* At this point module is disconnected from the STREAM and can
* no longer receive messages. Its put or service procedures are not
* running.
*/
rq->q_ptr = WR(rq)->q_ptr = NULL;
return (0);
}
This is pretty much the only code we need to write. The rest is the glue code that should be present in any module. Here is the description of this glue code.
Every source file should start with comments, so we shall start the nullmod.c with the appropriate comment:
/* * Nullmod: the minimal functioning STREAMS module. * * Copyright ..... (place your favorite one here). * */
We will need certain system include files:
/* * Required include files. */ #include <sys/types.h> #include <sys/conf.h> #include <sys/cred.h> #include <sys/ddi.h> #include <sys/modctl.h>
As we discussed before, our module will define only two functions -
nullmodopen() and nullmodclose():
/* * Function prototypes. */ static int nullmodopen(queue_t *, dev_t *, int, int, cred_t *); static int nullmodclose(queue_t *);
Every STREAMS kernel module should have a corresponding module_info(9S) structure:
static struct module_info nullmod_minfo = {
1, /* mi_idnum */
"nullmod", /* mi_idname */
0, /* mi_minpsz */
INFPSZ, /* mi_maxpsz */
0, /* mi_hiwat */
0 /* mi_lowat */
};
Also it should have qinit(9S) structures for both the read and write sides:
static struct qinit nullmod_rinit = {
(int (*)())putnext, /* qi_putp */
NULL, /* qi_srvp */
nullmodopen, /* qi_qopen */
nullmodclose, /* qi_qclose */
NULL, /* qi_qadmin */
&nullmod_minfo, /* qi_minfo */
};
static struct qinit nullmod_winit = {
(int (*)())putnext, /* qi_putp */
NULL, /* qi_srvp */
NULL, /* qi_qopen */
NULL, /* qi_qclose */
NULL, /* qi_qadmin */
&nullmod_minfo, /* qi_minfo */
};
The streamtab(9S) structure links both together:
static struct streamtab nullmod_info = {
&nullmod_rinit, /* st_rdinit */
&nullmod_winit, /* st_wrinit */
};
The fmodsw(9S) structure describes tqhe module to the operating system, providing the pointer to the streamtab structure above:
static struct fmodsw fsw = {
"nullmod", /* module name */
&nullmod_info, /* streams information */
D_MP /* module flags - multithreaded module */
};
Now we need to provide the linkage information for the module:
/*
* Module linkage information for the kernel.
*/
struct mod_ops mod_strmodops;
static struct modlstrmod modlstrmod = {
&mod_strmodops, "Example pass-through module 1.0", &fsw
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modlstrmod, NULL
};
Every loadable kernel module should also provide _init, _fini and _info entry points.
_init(9E) initializes a loadable module. It is called before any other routine in a loadable module. Most modules do not require any specific initialization and can just call mod_install(9F). _fini(9E) prepares a loadable module for unloading. It is called when the system wants to unload a module. In most cases it can just call mod_remove(9F). _info(9E) returns information about a loadable module. In most cases _info(9E) just returns the value returned by mod_info(9F).
/*
* Standard module entry points.
*/
int _init(void)
{
return (mod_install(&modlinkage));
}
int _fini(void)
{
return (mod_remove(&modlinkage));
}
int_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
Now we need to put our definitions for nullmodopen() and nullmodclose() above
and we are done! Our ``do-nothing'' module is ready.
Let us save the module in the file nullmod.c. To compile it we need a C compiler which can generate the code for the native kernel mode - 32-it for x86 and 64-bit for Sparc or AMD64 platforms. If we are using Sun compilers we can use the following commands to produce the module binary on sparc:
cc -c -D_KERNEL -D_SYSCALL32 -D_SYSCALL32_IMPL -xarch=v9 nullmod.c
Or with gcc
gcc -c -D_KERNEL -D_SYSCALL32 -D_SYSCALL32_IMPL -m64 nullmod.c
After that we need to use ld with -r option to produce the final binary:
ld -r -o nullmod nullmod.o
Now we need to copy our module to /usr/kernel/strmod/sparcv9 (you need to be a super-user for this):
# cp nullmod /usr/kernel/strmod/sparcv9
!!WARNING!!: you are going to install the kernel module. It is possible that the system will panic if there is some problem in the module. Please make sure that no one else is using the system and you will not upset anyone (including yourself) if the system panics as a result of your experiments. End of WARNING.
Now we can load the module:
# modload nullmod
And verify that it is loaded:
# modinfo | grep nullmod 147 7bbbde30 320 - 1 nullmod (Example pass-through module 1.0)
We can also insert our module onto STDIN for our shell:
# strchg -h nullmod
and verify that it is present:
# strconf nullmod ttcompat ldterm ptem pts
When we are done, we can remove the module from our shell STDIN:
# strchg -p # strconf ttcompat ldterm ptem pts
You can also unload the module (it is usually not rquired as Solaris unloads unused modules itself):
# modunload -i 147 # modinfo|grep nullmod #
Now you know how to
Why this module may be of any use? I can think of several reasons:
Not bad for a simple do-nothing program!
( May 13 2005, 09:06:15 PM PDT ) Permalink Comments [3]