Node: OpenBSD vfs interface, Up: Internal filesystem structures



OpenBSD vfs interface

The goal of vfs is to have a very clear separation between an independent filesystem and -dependent data structures and operations. This is done by using two layers, an independent filesystem one and an independent one. Surprising ! When adding a new filesystem in a vfs interface we simply needed to do the lower layer, the filesystem dependent routines. This low level code is then plugged into the vfs upper layer. The advantage is that in the lower layer you only need to code basic filesystem operations (check filesystem superbloc informations, write a file, read a file, ...). All the very complicated functions, in particular namei and lookup, does not needed to be rewrite.
The code of the upper layer is located in all the vfs_* files in kern/. the important structures definitions are in sys/namei.h and sys/vnode*.h. Just take a look at these files if you want to know how the kernel does the abstraction needed. A Compatible Filesystem Interface will also describes these structures that are the base of the vfs interface. The most important thing to remember is that the upper level only manipulates vnodes (virtual inodes) instead of inodes and blocks in the lower layer. The two layers communicates using vectors of operations that defined each filesystem. This is the main concept of vfs.
We only needed to know how work the interface of vfs toward a filesystem. Taking a look to sys/vnode.h, we can see a list of filesystem types. These tags are used in the vnode struct for knowing the type of filesystem we must use in the lower layer :

/*
 * Vnode tag types.
 * These are for the benefit of external programs only (e.g., pstat)
 * and should NEVER be inspected by the kernel.
 *
 * Note that v_tag is actually used to tell MFS from FFS, and EXT2FS from
 * the rest, so don't believe the above comment!
 */
enum vtagtype	{
  VT_NON, VT_UFS, VT_NFS, VT_MFS, VT_MSDOSFS, VT_LFS, VT_LOFS, VT_FDESC,
  VT_PORTAL, VT_NULL, VT_UMAP, VT_KERNFS, VT_PROCFS, VT_AFS, VT_ISOFS,
  VT_UNION, VT_ADOSFS, VT_EXT2FS, VT_NCPFS, VT_VFS, VT_XFS
};
...
struct vnode {
...
	enum	vtagtype v_tag;			/* type of underlying
        data */
...
};

The next file is sys/malloc.h. It contains the memory type information, you must provide a type for your new fs that will be used later :
/*
 * Types of memory to be allocated
 */
#define	M_FREE		0	/* should be on free list */
...
#define	M_EXT2FSNODE	72	/* EXT2FS vnode private part */
...

#define	INITKMEMNAMES { \
	"free",		/* 0 M_FREE */ \
...
	"EXT2FS node",	/* 72 M_EXT2FSNODE */ \
...
}

The last file for the interface is kern/vfs_conf.c. That file contains the global configuration of the vfs interface lower layer. The first thing is including external function definitions After that the array of functions pointers are built for the use of the filesystem. These structs are the vfsops (vfs operations; operations needed to initialise, mount, umount, sync, ...) and the vnodeops (virtual inode operations; operations that access inodes of the filesystem from the vnode given by the system like read, write, open, readlink, ...).

#ifdef FFS
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
#include <ufs/ffs/ffs_extern.h>
#endif

#ifdef EXT2FS
#include <ufs/ext2fs/ext2fs_extern.h>
#endif
...

/*
 * Set up the filesystem operations for vnodes.
 * The types are defined in mount.h.
 */
#ifdef FFS
extern	struct vfsops ffs_vfsops;
#endif
...

/*
 * Set up the filesystem operations for vnodes.
 */
static struct vfsconf vfsconflist[] = {

        /* Fast Filesystem */
#ifdef FFS
        { &ffs_vfsops, MOUNT_FFS, 1, 0, MNT_LOCAL, ffs_mountroot, NULL },
#endif
...
}

/*
 * vfs_opv_descs enumerates the list of vnode classes, each with it's own
 * vnode operation vector.  It is consulted at system boot to build operation
 * vectors.  It is NULL terminated.
 */
extern struct vnodeopv_desc ffs_vnodeop_opv_desc;
extern struct vnodeopv_desc ffs_specop_opv_desc;
extern struct vnodeopv_desc ffs_fifoop_opv_desc;
...
struct vnodeopv_desc *vfs_opv_descs[] = {
#ifdef FFS
	&ffs_vnodeop_opv_desc,
	&ffs_specop_opv_desc,
#ifdef FIFO
	&ffs_fifoop_opv_desc,
#endif
#endif


As you can see the upper-lower layers interface is very clear and easy to understand. This was one of the goals of the vfs interface and, in the case of OpenBSD, it's a good implementation.