Writing Filesystems - Module Glue Code
From Genunix
The Solaris Express developer documentation (like all older Solaris releases) has a manual called Writing Device Drivers, and another one that is more example-oriented called Device Driver Tutorial. These two books and other associated documentation show how to create block/character device driver modules, and describes what functionality the DDI (Device Driver Interface) provides for the use of these drivers to facilitate device register / memory and hardware access. I won't reiterate here what's in there, but will start with a comparison of how the module interfaces differ between character/block device drivers and filesystem modules, and end this section with the actual code snippets from the fat_vfsops.c sourcefile which contain the module linkage.
| character/block device drivers have ... | filesystem drivers have ... | int _init(void); int _fini(void); int _info(void); | Same prototypes for a filesystem drivers | Character/block device drivers initialize the xxx(9e) entry points statically by providing a cb_ops and a dev_ops function table, which is referenced directly via the data structure struct modldrv which identifies loadable device drivers to the Solaris kernel's module framework.
| Filesystem drivers interface with the Solaris kernel's module framework using a different module descriptor structure, struct modlfs (lfs, loadable filesystem). There is no longer any static function table passed in for filesystems, but instead only a vfsdef_t structure. This has two purposes:
Looks much different: | static struct cb_ops mydrv_cb_ops = {
...
};
static struct dev_ops mydrv_dev_ops = {
...
mydrv_cb_ops; /* dev_ops - (9e) vector */
...
};
/* modldrv structure */
static struct modldrv md = {
&mod_driverops, /* Module type - a driver. */
"dummy driver", /* Module name. */
&dummy_dev_ops
};
/* modlinkage structure */
static struct modlinkage ml = {
MODREV_1,
&md,
NULL
};
| static mntopt_t fatfs_mntopttbl[] = {
...
};
static mntopts_t fatfs_mntopt_prototype = {
sizeof (fatfs_mntopttbl) / sizeof (mntopt_t),
fatfs_mntopttbl
};
static int fatfsinit(int, char *);
static vfsdef_t vfw = {
VFSDEF_VERSION,
"fatfs",
fatfsinit,
VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS,
&fatfs_mntopt_prototype
};
static struct modlfs modlfs = {
&mod_fsops, "politically correct fs", &vfw
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modlfs, NULL
};
|
|---|
So unlike character/block device drivers, filesystem drivers do not provide any static function tables. The only statically-created information that's passed to the framework via struct modlfs / vfsdef_t is the table of mount options that are proprietary to this filesystem type. Anything else (!), in particular the function vectors for per-instance (VFS_) and per-node (VN_) operations, are created by the initialization hook, fatfsinit() in the case shown above.
Why do this ? Simple - it allows for changes to the framework, like modifications/extensions of the function call tables - i.e., provide new, previously undefined VFS_* and VN_* entry points.
