root/usr/src/uts/common/fs/fem.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <sys/types.h>
#include <sys/atomic.h>
#include <sys/kmem.h>
#include <sys/mutex.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
#include <sys/cmn_err.h>
#include <sys/debug.h>

#include <sys/fem.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/vfs_opreg.h>

#define NNODES_DEFAULT  8       /* Default number of nodes in a fem_list */
/*
 * fl_ntob(n) - Fem_list: number of nodes to bytes
 * Given the number of nodes in a fem_list return the size, in bytes,
 * of the fem_list structure.
 */
#define fl_ntob(n)      (sizeof (struct fem_list) + \
                        ((n) - 1) * sizeof (struct fem_node))

typedef enum {
        FEMTYPE_NULL,   /* Uninitialized */
        FEMTYPE_VNODE,
        FEMTYPE_VFS,
        FEMTYPE_NTYPES
} femtype_t;

#define FEM_HEAD(_t) femtype[(_t)].head.fn_op.anon
#define FEM_GUARD(_t) femtype[(_t)].guard

static struct fem_type_info {
        struct fem_node         head;
        struct fem_node         guard;
        femop_t                 *errf;
}       femtype[FEMTYPE_NTYPES];


/*
 * For each type, two tables - the translation offset definition, which
 * is used by fs_build_vector to layout the operation(s) vector; and the
 * guard_operation_vector which protects from stack under-run.
 */

int fem_err();
int fsem_err();


#define _FEMOPDEF(name, member)  \
        { VOPNAME_##name, offsetof(fem_t, femop_##member), NULL, fem_err }

static fs_operation_trans_def_t fem_opdef[] = {
        _FEMOPDEF(OPEN,         open),
        _FEMOPDEF(CLOSE,        close),
        _FEMOPDEF(READ,         read),
        _FEMOPDEF(WRITE,        write),
        _FEMOPDEF(IOCTL,        ioctl),
        _FEMOPDEF(SETFL,        setfl),
        _FEMOPDEF(GETATTR,      getattr),
        _FEMOPDEF(SETATTR,      setattr),
        _FEMOPDEF(ACCESS,       access),
        _FEMOPDEF(LOOKUP,       lookup),
        _FEMOPDEF(CREATE,       create),
        _FEMOPDEF(REMOVE,       remove),
        _FEMOPDEF(LINK,         link),
        _FEMOPDEF(RENAME,       rename),
        _FEMOPDEF(MKDIR,        mkdir),
        _FEMOPDEF(RMDIR,        rmdir),
        _FEMOPDEF(READDIR,      readdir),
        _FEMOPDEF(SYMLINK,      symlink),
        _FEMOPDEF(READLINK,     readlink),
        _FEMOPDEF(FSYNC,        fsync),
        _FEMOPDEF(INACTIVE,     inactive),
        _FEMOPDEF(FID,          fid),
        _FEMOPDEF(RWLOCK,       rwlock),
        _FEMOPDEF(RWUNLOCK,     rwunlock),
        _FEMOPDEF(SEEK,         seek),
        _FEMOPDEF(CMP,          cmp),
        _FEMOPDEF(FRLOCK,       frlock),
        _FEMOPDEF(SPACE,        space),
        _FEMOPDEF(REALVP,       realvp),
        _FEMOPDEF(GETPAGE,      getpage),
        _FEMOPDEF(PUTPAGE,      putpage),
        _FEMOPDEF(MAP,          map),
        _FEMOPDEF(ADDMAP,       addmap),
        _FEMOPDEF(DELMAP,       delmap),
        _FEMOPDEF(POLL,         poll),
        _FEMOPDEF(DUMP,         dump),
        _FEMOPDEF(PATHCONF,     pathconf),
        _FEMOPDEF(PAGEIO,       pageio),
        _FEMOPDEF(DUMPCTL,      dumpctl),
        _FEMOPDEF(DISPOSE,      dispose),
        _FEMOPDEF(SETSECATTR,   setsecattr),
        _FEMOPDEF(GETSECATTR,   getsecattr),
        _FEMOPDEF(SHRLOCK,      shrlock),
        _FEMOPDEF(VNEVENT,      vnevent),
        _FEMOPDEF(REQZCBUF,     reqzcbuf),
        _FEMOPDEF(RETZCBUF,     retzcbuf),
        { NULL, 0, NULL, NULL }
};


#define _FEMGUARD(name, ignore)  \
        { VOPNAME_##name, (femop_t *)fem_err }

static struct fs_operation_def fem_guard_ops[] = {
        _FEMGUARD(OPEN,         open),
        _FEMGUARD(CLOSE,        close),
        _FEMGUARD(READ,         read),
        _FEMGUARD(WRITE,        write),
        _FEMGUARD(IOCTL,        ioctl),
        _FEMGUARD(SETFL,        setfl),
        _FEMGUARD(GETATTR,      getattr),
        _FEMGUARD(SETATTR,      setattr),
        _FEMGUARD(ACCESS,       access),
        _FEMGUARD(LOOKUP,       lookup),
        _FEMGUARD(CREATE,       create),
        _FEMGUARD(REMOVE,       remove),
        _FEMGUARD(LINK,         link),
        _FEMGUARD(RENAME,       rename),
        _FEMGUARD(MKDIR,        mkdir),
        _FEMGUARD(RMDIR,        rmdir),
        _FEMGUARD(READDIR,      readdir),
        _FEMGUARD(SYMLINK,      symlink),
        _FEMGUARD(READLINK,     readlink),
        _FEMGUARD(FSYNC,        fsync),
        _FEMGUARD(INACTIVE,     inactive),
        _FEMGUARD(FID,          fid),
        _FEMGUARD(RWLOCK,       rwlock),
        _FEMGUARD(RWUNLOCK,     rwunlock),
        _FEMGUARD(SEEK,         seek),
        _FEMGUARD(CMP,          cmp),
        _FEMGUARD(FRLOCK,       frlock),
        _FEMGUARD(SPACE,        space),
        _FEMGUARD(REALVP,       realvp),
        _FEMGUARD(GETPAGE,      getpage),
        _FEMGUARD(PUTPAGE,      putpage),
        _FEMGUARD(MAP,          map),
        _FEMGUARD(ADDMAP,       addmap),
        _FEMGUARD(DELMAP,       delmap),
        _FEMGUARD(POLL,         poll),
        _FEMGUARD(DUMP,         dump),
        _FEMGUARD(PATHCONF,     pathconf),
        _FEMGUARD(PAGEIO,       pageio),
        _FEMGUARD(DUMPCTL,      dumpctl),
        _FEMGUARD(DISPOSE,      dispose),
        _FEMGUARD(SETSECATTR,   setsecattr),
        _FEMGUARD(GETSECATTR,   getsecattr),
        _FEMGUARD(SHRLOCK,      shrlock),
        _FEMGUARD(VNEVENT,      vnevent),
        _FEMGUARD(REQZCBUF,     reqzcbuf),
        _FEMGUARD(RETZCBUF,     retzcbuf),
        { NULL, NULL }
};


#define _FSEMOPDEF(name, member)  \
        { VFSNAME_##name, offsetof(fsem_t, fsemop_##member), NULL, fsem_err }

static fs_operation_trans_def_t fsem_opdef[] = {
        _FSEMOPDEF(MOUNT,       mount),
        _FSEMOPDEF(UNMOUNT,     unmount),
        _FSEMOPDEF(ROOT,        root),
        _FSEMOPDEF(STATVFS,     statvfs),
        _FSEMOPDEF(SYNC,        sync),
        _FSEMOPDEF(VGET,        vget),
        _FSEMOPDEF(MOUNTROOT,   mountroot),
        _FSEMOPDEF(FREEVFS,     freevfs),
        _FSEMOPDEF(VNSTATE,     vnstate),
        _FSEMOPDEF(SYNCFS,      syncfs),
        { NULL, 0, NULL, NULL }
};

#define _FSEMGUARD(name, ignore)  \
        { VFSNAME_##name, (femop_t *)fsem_err }

static struct fs_operation_def fsem_guard_ops[] = {
        _FSEMGUARD(MOUNT,       mount),
        _FSEMGUARD(UNMOUNT,     unmount),
        _FSEMGUARD(ROOT,        root),
        _FSEMGUARD(STATVFS,     statvfs),
        _FSEMGUARD(SYNC,        sync),
        _FSEMGUARD(VGET,        vget),
        _FSEMGUARD(MOUNTROOT,   mountroot),
        _FSEMGUARD(FREEVFS,     freevfs),
        _FSEMGUARD(VNSTATE,     vnstate),
        _FSEMGUARD(SYNCFS,      syncfs),
        { NULL, NULL}
};


/*
 * vsop_find, vfsop_find -
 *
 * These macros descend the stack until they find either a basic
 * vnode/vfs operation [ indicated by a null fn_available ] or a
 * stacked item where this method is non-null [_vsop].
 *
 * The DEBUG one is written with a single function which manually applies
 * the structure offsets.  It can have additional debugging support.
 */

#ifndef DEBUG

#define vsop_find(ap, func, funct, arg0, _vop, _vsop) \
for (;;) { \
        if ((ap)->fa_fnode->fn_available == NULL) { \
                *(func) = (funct (*)())((ap)->fa_fnode->fn_op.vnode->_vop); \
                *(arg0) = (void *)(ap)->fa_vnode.vp; \
                break;  \
        } else if ((*(func) = (funct (*)())((ap)->fa_fnode->fn_op.fem->_vsop))\
                    != NULL) { \
                *(arg0) = (void *) (ap); \
                break;  \
        } else { \
                (ap)->fa_fnode--; \
        } \
} \

#define vfsop_find(ap, func, funct, arg0, _vop, _vsop) \
for (;;) { \
        if ((ap)->fa_fnode->fn_available == NULL) { \
                *(func) = (funct (*)())((ap)->fa_fnode->fn_op.vfs->_vop); \
                *(arg0) = (void *)(ap)->fa_vnode.vp; \
                break; \
        } else if ((*(func) = (funct (*)())((ap)->fa_fnode->fn_op.fsem->_vsop))\
                    != NULL) { \
                *(arg0) = (void *) (ap); \
                break; \
        } else { \
                (ap)->fa_fnode--; \
        } \
} \

#else

#define vsop_find(ap, func, funct, arg0, _vop, _vsop) \
        *(arg0) = _op_find((ap), (void **)(func), \
                        offsetof(vnodeops_t, _vop), offsetof(fem_t, _vsop))

#define vfsop_find(ap, func, funct, arg0, _fop, _fsop) \
        *(arg0) = _op_find((ap), (void **)(func), \
                        offsetof(vfsops_t, _fop), offsetof(fsem_t, _fsop))

static void *
_op_find(femarg_t *ap, void **fp, int offs0, int offs1)
{
        void *ptr;
        for (;;) {
                struct fem_node *fnod = ap->fa_fnode;
                if (fnod->fn_available == NULL) {
                        *fp = *(void **)((char *)fnod->fn_op.anon + offs0);
                        ptr = (void *)(ap->fa_vnode.anon);
                        break;
                } else if ((*fp = *(void **)((char *)fnod->fn_op.anon+offs1))
                    != NULL) {
                        ptr = (void *)(ap);
                        break;
                } else {
                        ap->fa_fnode--;
                }
        }
        return (ptr);
}
#endif

static fem_t *
fem_alloc(void)
{
        fem_t   *p;

        p = (fem_t *)kmem_alloc(sizeof (*p), KM_SLEEP);
        return (p);
}

void
fem_free(fem_t *p)
{
        kmem_free(p, sizeof (*p));
}

static fsem_t *
fsem_alloc(void)
{
        fsem_t  *p;

        p = (fsem_t *)kmem_alloc(sizeof (*p), KM_SLEEP);
        return (p);
}

void
fsem_free(fsem_t *p)
{
        kmem_free(p, sizeof (*p));
}


/*
 * fem_get, fem_release - manage reference counts on the stack.
 *
 * The list of monitors can be updated while operations are in
 * progress on the object.
 *
 * The reference count facilitates this by counting the number of
 * current accessors, and deconstructing the list when it is exhausted.
 *
 * fem_lock() is required to:
 *      look at femh_list
 *      update what femh_list points to
 *      update femh_list
 *      increase femh_list->feml_refc.
 *
 * the feml_refc can decrement without holding the lock;
 * when feml_refc becomes zero, the list is destroyed.
 *
 */

static struct fem_list *
fem_lock(struct fem_head *fp)
{
        struct fem_list *sp = NULL;

        ASSERT(fp != NULL);
        mutex_enter(&fp->femh_lock);
        sp = fp->femh_list;
        return (sp);
}

static void
fem_unlock(struct fem_head *fp)
{
        ASSERT(fp != NULL);
        mutex_exit(&fp->femh_lock);
}

/*
 * Addref can only be called while its head->lock is held.
 */

static void
fem_addref(struct fem_list *sp)
{
        atomic_inc_32(&sp->feml_refc);
}

static uint32_t
fem_delref(struct fem_list *sp)
{
        return (atomic_dec_32_nv(&sp->feml_refc));
}

static struct fem_list *
fem_get(struct fem_head *fp)
{
        struct fem_list *sp = NULL;

        if (fp != NULL) {
                if ((sp = fem_lock(fp)) != NULL) {
                        fem_addref(sp);
                }
                fem_unlock(fp);
        }
        return (sp);
}

static void
fem_release(struct fem_list *sp)
{
        int     i;

        ASSERT(sp->feml_refc != 0);
        if (fem_delref(sp) == 0) {
                /*
                 * Before freeing the list, we need to release the
                 * caller-provided data.
                 */
                for (i = sp->feml_tos; i > 0; i--) {
                        struct fem_node *fnp = &sp->feml_nodes[i];

                        if (fnp->fn_av_rele)
                                (*(fnp->fn_av_rele))(fnp->fn_available);
                }
                kmem_free(sp, fl_ntob(sp->feml_ssize));
        }
}


/*
 * These are the 'head' operations which perform the interposition.
 *
 * This set must be 1:1, onto with the (vnodeops, vfsos).
 *
 * If there is a desire to globally disable interposition for a particular
 * method, the corresponding 'head' routine should unearth the base method
 * and invoke it directly rather than bypassing the function.
 *
 * All the functions are virtually the same, save for names, types & args.
 *  1. get a reference to the monitor stack for this object.
 *  2. store the top of stack into the femarg structure.
 *  3. store the basic object (vnode *, vnode **, vfs *) in the femarg struc.
 *  4. invoke the "top" method for this object.
 *  5. release the reference to the monitor stack.
 *
 */

static int
vhead_open(vnode_t **vpp, int mode, cred_t *cr, caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock((*vpp)->v_femhead)) == NULL) {
                func = (int (*)()) ((*vpp)->v_op->vop_open);
                arg0 = (void *)vpp;
                fem_unlock((*vpp)->v_femhead);
                errc = (*func)(arg0, mode, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock((*vpp)->v_femhead);
                farg.fa_vnode.vpp = vpp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_open, femop_open);
                errc = (*func)(arg0, mode, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
    caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_close);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, flag, count, offset, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_close, femop_close);
                errc = (*func)(arg0, flag, count, offset, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
    caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_read);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, uiop, ioflag, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_read, femop_read);
                errc = (*func)(arg0, uiop, ioflag, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
    caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_write);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, uiop, ioflag, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_write, femop_write);
                errc = (*func)(arg0, uiop, ioflag, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr,
    int *rvalp, caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_ioctl);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, cmd, arg, flag, cr, rvalp, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_ioctl, femop_ioctl);
                errc = (*func)(arg0, cmd, arg, flag, cr, rvalp, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_setfl(vnode_t *vp, int oflags, int nflags, cred_t *cr,
    caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_setfl);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, oflags, nflags, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_setfl, femop_setfl);
                errc = (*func)(arg0, oflags, nflags, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
    caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_getattr);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, vap, flags, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_getattr,
                    femop_getattr);
                errc = (*func)(arg0, vap, flags, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
    caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_setattr);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, vap, flags, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_setattr,
                    femop_setattr);
                errc = (*func)(arg0, vap, flags, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_access(vnode_t *vp, int mode, int flags, cred_t *cr,
    caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_access);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, mode, flags, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_access,
                    femop_access);
                errc = (*func)(arg0, mode, flags, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp,
    int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
    int *direntflags, pathname_t *realpnp)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(dvp->v_femhead)) == NULL) {
                func = (int (*)()) (dvp->v_op->vop_lookup);
                arg0 = dvp;
                fem_unlock(dvp->v_femhead);
                errc = (*func)(arg0, nm, vpp, pnp, flags, rdir, cr, ct,
                    direntflags, realpnp);
        } else {
                fem_addref(femsp);
                fem_unlock(dvp->v_femhead);
                farg.fa_vnode.vp = dvp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_lookup,
                    femop_lookup);
                errc = (*func)(arg0, nm, vpp, pnp, flags, rdir, cr, ct,
                    direntflags, realpnp);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl,
    int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct,
    vsecattr_t *vsecp)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(dvp->v_femhead)) == NULL) {
                func = (int (*)()) (dvp->v_op->vop_create);
                arg0 = dvp;
                fem_unlock(dvp->v_femhead);
                errc = (*func)(arg0, name, vap, excl, mode, vpp, cr, flag,
                    ct, vsecp);
        } else {
                fem_addref(femsp);
                fem_unlock(dvp->v_femhead);
                farg.fa_vnode.vp = dvp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_create,
                    femop_create);
                errc = (*func)(arg0, name, vap, excl, mode, vpp, cr, flag,
                    ct, vsecp);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
    int flags)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(dvp->v_femhead)) == NULL) {
                func = (int (*)()) (dvp->v_op->vop_remove);
                arg0 = dvp;
                fem_unlock(dvp->v_femhead);
                errc = (*func)(arg0, nm, cr, ct, flags);
        } else {
                fem_addref(femsp);
                fem_unlock(dvp->v_femhead);
                farg.fa_vnode.vp = dvp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_remove,
                    femop_remove);
                errc = (*func)(arg0, nm, cr, ct, flags);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr,
    caller_context_t *ct, int flags)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(tdvp->v_femhead)) == NULL) {
                func = (int (*)()) (tdvp->v_op->vop_link);
                arg0 = tdvp;
                fem_unlock(tdvp->v_femhead);
                errc = (*func)(arg0, svp, tnm, cr, ct, flags);
        } else {
                fem_addref(femsp);
                fem_unlock(tdvp->v_femhead);
                farg.fa_vnode.vp = tdvp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_link, femop_link);
                errc = (*func)(arg0, svp, tnm, cr, ct, flags);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
    cred_t *cr, caller_context_t *ct, int flags)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(sdvp->v_femhead)) == NULL) {
                func = (int (*)()) (sdvp->v_op->vop_rename);
                arg0 = sdvp;
                fem_unlock(sdvp->v_femhead);
                errc = (*func)(arg0, snm, tdvp, tnm, cr, ct, flags);
        } else {
                fem_addref(femsp);
                fem_unlock(sdvp->v_femhead);
                farg.fa_vnode.vp = sdvp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_rename,
                    femop_rename);
                errc = (*func)(arg0, snm, tdvp, tnm, cr, ct, flags);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp,
    cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(dvp->v_femhead)) == NULL) {
                func = (int (*)()) (dvp->v_op->vop_mkdir);
                arg0 = dvp;
                fem_unlock(dvp->v_femhead);
                errc = (*func)(arg0, dirname, vap, vpp, cr, ct, flags, vsecp);
        } else {
                fem_addref(femsp);
                fem_unlock(dvp->v_femhead);
                farg.fa_vnode.vp = dvp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_mkdir, femop_mkdir);
                errc = (*func)(arg0, dirname, vap, vpp, cr, ct, flags, vsecp);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr,
    caller_context_t *ct, int flags)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(dvp->v_femhead)) == NULL) {
                func = (int (*)()) (dvp->v_op->vop_rmdir);
                arg0 = dvp;
                fem_unlock(dvp->v_femhead);
                errc = (*func)(arg0, nm, cdir, cr, ct, flags);
        } else {
                fem_addref(femsp);
                fem_unlock(dvp->v_femhead);
                farg.fa_vnode.vp = dvp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_rmdir, femop_rmdir);
                errc = (*func)(arg0, nm, cdir, cr, ct, flags);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp,
    caller_context_t *ct, int flags)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_readdir);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, uiop, cr, eofp, ct, flags);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_readdir,
                    femop_readdir);
                errc = (*func)(arg0, uiop, cr, eofp, ct, flags);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_symlink(vnode_t *dvp, char *linkname, vattr_t *vap, char *target,
    cred_t *cr, caller_context_t *ct, int flags)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(dvp->v_femhead)) == NULL) {
                func = (int (*)()) (dvp->v_op->vop_symlink);
                arg0 = dvp;
                fem_unlock(dvp->v_femhead);
                errc = (*func)(arg0, linkname, vap, target, cr, ct, flags);
        } else {
                fem_addref(femsp);
                fem_unlock(dvp->v_femhead);
                farg.fa_vnode.vp = dvp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_symlink,
                    femop_symlink);
                errc = (*func)(arg0, linkname, vap, target, cr, ct, flags);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr, caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_readlink);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, uiop, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_readlink,
                    femop_readlink);
                errc = (*func)(arg0, uiop, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_fsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_fsync);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, syncflag, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_fsync, femop_fsync);
                errc = (*func)(arg0, syncflag, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        void            (*func)();
        void            *arg0;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (void (*)()) (vp->v_op->vop_inactive);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                (*func)(arg0, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, void, &arg0, vop_inactive,
                    femop_inactive);
                (*func)(arg0, cr, ct);
                fem_release(femsp);
        }
        return (0);
}

static int
vhead_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_fid);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, fidp, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_fid, femop_fid);
                errc = (*func)(arg0, fidp, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_rwlock(vnode_t *vp, int write_lock, caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_rwlock);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, write_lock, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_rwlock,
                    femop_rwlock);
                errc = (*func)(arg0, write_lock, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_rwunlock(vnode_t *vp, int write_lock, caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        void            (*func)();
        void            *arg0;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (void (*)()) (vp->v_op->vop_rwunlock);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                (*func)(arg0, write_lock, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, void, &arg0, vop_rwunlock,
                    femop_rwunlock);
                (*func)(arg0, write_lock, ct);
                fem_release(femsp);
        }
        return (0);
}

static int
vhead_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_seek);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, ooff, noffp, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_seek, femop_seek);
                errc = (*func)(arg0, ooff, noffp, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_cmp(vnode_t *vp1, vnode_t *vp2, caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp1->v_femhead)) == NULL) {
                func = (int (*)()) (vp1->v_op->vop_cmp);
                arg0 = vp1;
                fem_unlock(vp1->v_femhead);
                errc = (*func)(arg0, vp2, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp1->v_femhead);
                farg.fa_vnode.vp = vp1;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_cmp, femop_cmp);
                errc = (*func)(arg0, vp2, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_frlock(vnode_t *vp, int cmd, struct flock64 *bfp, int flag,
    offset_t offset, struct flk_callback *flk_cbp, cred_t *cr,
    caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_frlock);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, cmd, bfp, flag, offset, flk_cbp, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_frlock,
                    femop_frlock);
                errc = (*func)(arg0, cmd, bfp, flag, offset, flk_cbp, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag,
    offset_t offset, cred_t *cr, caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_space);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, cmd, bfp, flag, offset, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_space, femop_space);
                errc = (*func)(arg0, cmd, bfp, flag, offset, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_realvp);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, vpp, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_realvp,
                    femop_realvp);
                errc = (*func)(arg0, vpp, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_getpage(vnode_t *vp, offset_t off, size_t len, uint_t *protp,
    struct page **plarr, size_t plsz, struct seg *seg, caddr_t addr,
    enum seg_rw rw, cred_t *cr, caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_getpage);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, off, len, protp, plarr, plsz, seg,
                    addr, rw, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_getpage,
                    femop_getpage);
                errc = (*func)(arg0, off, len, protp, plarr, plsz, seg,
                    addr, rw, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr,
    caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_putpage);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, off, len, flags, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_putpage,
                    femop_putpage);
                errc = (*func)(arg0, off, len, flags, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp,
    size_t len, uchar_t prot, uchar_t maxprot, uint_t flags,
    cred_t *cr, caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_map);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, off, as, addrp, len, prot, maxprot,
                    flags, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_map, femop_map);
                errc = (*func)(arg0, off, as, addrp, len, prot, maxprot,
                    flags, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr,
    size_t len, uchar_t prot, uchar_t maxprot, uint_t flags,
    cred_t *cr, caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_addmap);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, off, as, addr, len, prot, maxprot,
                    flags, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_addmap,
                    femop_addmap);
                errc = (*func)(arg0, off, as, addr, len, prot, maxprot,
                    flags, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr,
    size_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr,
    caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_delmap);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, off, as, addr, len, prot, maxprot,
                    flags, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_delmap,
                    femop_delmap);
                errc = (*func)(arg0, off, as, addr, len, prot, maxprot,
                    flags, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_poll(vnode_t *vp, short events, int anyyet, short *reventsp,
    struct pollhead **phpp, caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_poll);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, events, anyyet, reventsp, phpp, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_poll, femop_poll);
                errc = (*func)(arg0, events, anyyet, reventsp, phpp, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_dump(vnode_t *vp, caddr_t addr, offset_t lbdn, offset_t dblks,
    caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_dump);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, addr, lbdn, dblks, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_dump, femop_dump);
                errc = (*func)(arg0, addr, lbdn, dblks, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
    caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_pathconf);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, cmd, valp, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_pathconf,
                    femop_pathconf);
                errc = (*func)(arg0, cmd, valp, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_pageio(vnode_t *vp, struct page *pp, u_offset_t io_off,
    size_t io_len, int flags, cred_t *cr, caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_pageio);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, pp, io_off, io_len, flags, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_pageio,
                    femop_pageio);
                errc = (*func)(arg0, pp, io_off, io_len, flags, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_dumpctl(vnode_t *vp, int action, offset_t *blkp, caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_dumpctl);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, action, blkp, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_dumpctl,
                    femop_dumpctl);
                errc = (*func)(arg0, action, blkp, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_dispose(vnode_t *vp, struct page *pp, int flag, int dn, cred_t *cr,
    caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        void            (*func)();
        void            *arg0;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (void (*)()) (vp->v_op->vop_dispose);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                (*func)(arg0, pp, flag, dn, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, void, &arg0, vop_dispose,
                    femop_dispose);
                (*func)(arg0, pp, flag, dn, cr, ct);
                fem_release(femsp);
        }
        return (0);
}

static int
vhead_setsecattr(vnode_t *vp, vsecattr_t *vsap, int flag, cred_t *cr,
    caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_setsecattr);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, vsap, flag, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_setsecattr,
                    femop_setsecattr);
                errc = (*func)(arg0, vsap, flag, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_getsecattr(vnode_t *vp, vsecattr_t *vsap, int flag, cred_t *cr,
    caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_getsecattr);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, vsap, flag, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_getsecattr,
                    femop_getsecattr);
                errc = (*func)(arg0, vsap, flag, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag,
    cred_t *cr, caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_shrlock);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, cmd, shr, flag, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_shrlock,
                    femop_shrlock);
                errc = (*func)(arg0, cmd, shr, flag, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_vnevent(vnode_t *vp, vnevent_t vnevent, vnode_t *dvp, char *cname,
    caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_vnevent);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, vnevent, dvp, cname, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_vnevent,
                    femop_vnevent);
                errc = (*func)(arg0, vnevent, dvp, cname, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_reqzcbuf(vnode_t *vp, enum uio_rw ioflag, xuio_t *xuiop, cred_t *cr,
    caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_reqzcbuf);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, ioflag, xuiop, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_reqzcbuf,
                    femop_reqzcbuf);
                errc = (*func)(arg0, ioflag, xuiop, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
vhead_retzcbuf(vnode_t *vp, xuio_t *xuiop, cred_t *cr, caller_context_t *ct)
{
        femarg_t        farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        if ((femsp = fem_lock(vp->v_femhead)) == NULL) {
                func = (int (*)()) (vp->v_op->vop_retzcbuf);
                arg0 = vp;
                fem_unlock(vp->v_femhead);
                errc = (*func)(arg0, xuiop, cr, ct);
        } else {
                fem_addref(femsp);
                fem_unlock(vp->v_femhead);
                farg.fa_vnode.vp = vp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vsop_find(&farg, &func, int, &arg0, vop_retzcbuf,
                    femop_retzcbuf);
                errc = (*func)(arg0, xuiop, cr, ct);
                fem_release(femsp);
        }
        return (errc);
}

static int
fshead_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
{
        fsemarg_t       farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        ASSERT(vfsp->vfs_implp);

        if ((femsp = fem_lock(vfsp->vfs_femhead)) == NULL) {
                func = (int (*)()) vfsp->vfs_op->vfs_mount;
                fem_unlock(vfsp->vfs_femhead);
                errc = (*func)(vfsp, mvp, uap, cr);
        } else {
                fem_addref(femsp);
                fem_unlock(vfsp->vfs_femhead);
                farg.fa_vnode.vfsp = vfsp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vfsop_find(&farg, &func, int, &arg0, vfs_mount,
                    fsemop_mount);
                errc = (*func)(arg0, mvp, uap, cr);
                fem_release(femsp);
        }
        return (errc);
}

static int
fshead_unmount(vfs_t *vfsp, int flag, cred_t *cr)
{
        fsemarg_t       farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        ASSERT(vfsp->vfs_implp);

        if ((femsp = fem_lock(vfsp->vfs_femhead)) == NULL) {
                func = (int (*)()) vfsp->vfs_op->vfs_unmount;
                fem_unlock(vfsp->vfs_femhead);
                errc = (*func)(vfsp, flag, cr);
        } else {
                fem_addref(femsp);
                fem_unlock(vfsp->vfs_femhead);
                farg.fa_vnode.vfsp = vfsp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vfsop_find(&farg, &func, int, &arg0, vfs_unmount,
                    fsemop_unmount);
                errc = (*func)(arg0, flag, cr);
                fem_release(femsp);
        }
        return (errc);
}

static int
fshead_root(vfs_t *vfsp, vnode_t **vpp)
{
        fsemarg_t       farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        ASSERT(vfsp->vfs_implp);

        if ((femsp = fem_lock(vfsp->vfs_femhead)) == NULL) {
                func = (int (*)()) vfsp->vfs_op->vfs_root;
                fem_unlock(vfsp->vfs_femhead);
                errc = (*func)(vfsp, vpp);
        } else {
                fem_addref(femsp);
                fem_unlock(vfsp->vfs_femhead);
                farg.fa_vnode.vfsp = vfsp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vfsop_find(&farg, &func, int, &arg0, vfs_root, fsemop_root);
                errc = (*func)(arg0, vpp);
                fem_release(femsp);
        }
        return (errc);
}

static int
fshead_statvfs(vfs_t *vfsp, statvfs64_t *sp)
{
        fsemarg_t       farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        ASSERT(vfsp->vfs_implp);

        if ((femsp = fem_lock(vfsp->vfs_femhead)) == NULL) {
                func = (int (*)()) vfsp->vfs_op->vfs_statvfs;
                fem_unlock(vfsp->vfs_femhead);
                errc = (*func)(vfsp, sp);
        } else {
                fem_addref(femsp);
                fem_unlock(vfsp->vfs_femhead);
                farg.fa_vnode.vfsp = vfsp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vfsop_find(&farg, &func, int, &arg0, vfs_statvfs,
                    fsemop_statvfs);
                errc = (*func)(arg0, sp);
                fem_release(femsp);
        }
        return (errc);
}

static int
fshead_sync(vfs_t *vfsp, short flag, cred_t *cr)
{
        fsemarg_t       farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        ASSERT(vfsp->vfs_implp);

        if ((femsp = fem_lock(vfsp->vfs_femhead)) == NULL) {
                func = (int (*)()) vfsp->vfs_op->vfs_sync;
                fem_unlock(vfsp->vfs_femhead);
                errc = (*func)(vfsp, flag, cr);
        } else {
                fem_addref(femsp);
                fem_unlock(vfsp->vfs_femhead);
                farg.fa_vnode.vfsp = vfsp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vfsop_find(&farg, &func, int, &arg0, vfs_sync, fsemop_sync);
                errc = (*func)(arg0, flag, cr);
                fem_release(femsp);
        }
        return (errc);
}

static int
fshead_syncfs(vfs_t *vfsp, short flag, cred_t *cr)
{
        fsemarg_t       farg;
        struct fem_list *femsp;
        int             (*func)(vfs_t *, uint64_t, cred_t *);
        void            *arg0;
        int             errc;

        ASSERT(vfsp->vfs_implp);

        if ((femsp = fem_lock(vfsp->vfs_femhead)) == NULL) {
                func = vfsp->vfs_op->vfs_syncfs;
                fem_unlock(vfsp->vfs_femhead);
                errc = (*func)(vfsp, flag, cr);
        } else {
                fem_addref(femsp);
                fem_unlock(vfsp->vfs_femhead);
                farg.fa_vnode.vfsp = vfsp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vfsop_find(&farg, &func, int, &arg0, vfs_syncfs, fsemop_syncfs);
                errc = (*func)(arg0, flag, cr);
                fem_release(femsp);
        }
        return (errc);
}


static int
fshead_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
{
        fsemarg_t       farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        ASSERT(vfsp->vfs_implp);

        if ((femsp = fem_lock(vfsp->vfs_femhead)) == NULL) {
                func = (int (*)()) vfsp->vfs_op->vfs_vget;
                fem_unlock(vfsp->vfs_femhead);
                errc = (*func)(vfsp, vpp, fidp);
        } else {
                fem_addref(femsp);
                fem_unlock(vfsp->vfs_femhead);
                farg.fa_vnode.vfsp = vfsp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vfsop_find(&farg, &func, int, &arg0, vfs_vget, fsemop_vget);
                errc = (*func)(arg0, vpp, fidp);
                fem_release(femsp);
        }
        return (errc);
}

static int
fshead_mountroot(vfs_t *vfsp, enum whymountroot reason)
{
        fsemarg_t       farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        ASSERT(vfsp->vfs_implp);

        if ((femsp = fem_lock(vfsp->vfs_femhead)) == NULL) {
                func = (int (*)()) vfsp->vfs_op->vfs_mountroot;
                fem_unlock(vfsp->vfs_femhead);
                errc = (*func)(vfsp, reason);
        } else {
                fem_addref(femsp);
                fem_unlock(vfsp->vfs_femhead);
                farg.fa_vnode.vfsp = vfsp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vfsop_find(&farg, &func, int, &arg0, vfs_mountroot,
                    fsemop_mountroot);
                errc = (*func)(arg0, reason);
                fem_release(femsp);
        }
        return (errc);
}

static int
fshead_freevfs(vfs_t *vfsp)
{
        fsemarg_t       farg;
        struct fem_list *femsp;
        void            (*func)();
        void            *arg0;

        ASSERT(vfsp->vfs_implp);

        if ((femsp = fem_lock(vfsp->vfs_femhead)) == NULL) {
                func = (void (*)()) vfsp->vfs_op->vfs_freevfs;
                fem_unlock(vfsp->vfs_femhead);
                (*func)(vfsp);
        } else {
                fem_addref(femsp);
                fem_unlock(vfsp->vfs_femhead);
                farg.fa_vnode.vfsp = vfsp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vfsop_find(&farg, &func, void, &arg0, vfs_freevfs,
                    fsemop_freevfs);
                (*func)(arg0);
                fem_release(femsp);
        }
        return (0);
}

static int
fshead_vnstate(vfs_t *vfsp, vnode_t *vp, vntrans_t nstate)
{
        fsemarg_t       farg;
        struct fem_list *femsp;
        int             (*func)();
        void            *arg0;
        int             errc;

        ASSERT(vfsp->vfs_implp);

        if ((femsp = fem_lock(vfsp->vfs_femhead)) == NULL) {
                func = (int (*)()) vfsp->vfs_op->vfs_vnstate;
                fem_unlock(vfsp->vfs_femhead);
                errc = (*func)(vfsp, vp, nstate);
        } else {
                fem_addref(femsp);
                fem_unlock(vfsp->vfs_femhead);
                farg.fa_vnode.vfsp = vfsp;
                farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos;
                vfsop_find(&farg, &func, int, &arg0, vfs_vnstate,
                    fsemop_vnstate);
                errc = (*func)(arg0, vp, nstate);
                fem_release(femsp);
        }
        return (errc);
}


/*
 * specification table for the vhead vnode operations.
 * It is an error for any operations to be missing.
 */

static struct fs_operation_def fhead_vn_spec[] = {
        { VOPNAME_OPEN, (femop_t *)vhead_open },
        { VOPNAME_CLOSE, (femop_t *)vhead_close },
        { VOPNAME_READ, (femop_t *)vhead_read },
        { VOPNAME_WRITE, (femop_t *)vhead_write },
        { VOPNAME_IOCTL, (femop_t *)vhead_ioctl },
        { VOPNAME_SETFL, (femop_t *)vhead_setfl },
        { VOPNAME_GETATTR, (femop_t *)vhead_getattr },
        { VOPNAME_SETATTR, (femop_t *)vhead_setattr },
        { VOPNAME_ACCESS, (femop_t *)vhead_access },
        { VOPNAME_LOOKUP, (femop_t *)vhead_lookup },
        { VOPNAME_CREATE, (femop_t *)vhead_create },
        { VOPNAME_REMOVE, (femop_t *)vhead_remove },
        { VOPNAME_LINK, (femop_t *)vhead_link },
        { VOPNAME_RENAME, (femop_t *)vhead_rename },
        { VOPNAME_MKDIR, (femop_t *)vhead_mkdir },
        { VOPNAME_RMDIR, (femop_t *)vhead_rmdir },
        { VOPNAME_READDIR, (femop_t *)vhead_readdir },
        { VOPNAME_SYMLINK, (femop_t *)vhead_symlink },
        { VOPNAME_READLINK, (femop_t *)vhead_readlink },
        { VOPNAME_FSYNC, (femop_t *)vhead_fsync },
        { VOPNAME_INACTIVE, (femop_t *)vhead_inactive },
        { VOPNAME_FID, (femop_t *)vhead_fid },
        { VOPNAME_RWLOCK, (femop_t *)vhead_rwlock },
        { VOPNAME_RWUNLOCK, (femop_t *)vhead_rwunlock },
        { VOPNAME_SEEK, (femop_t *)vhead_seek },
        { VOPNAME_CMP, (femop_t *)vhead_cmp },
        { VOPNAME_FRLOCK, (femop_t *)vhead_frlock },
        { VOPNAME_SPACE, (femop_t *)vhead_space },
        { VOPNAME_REALVP, (femop_t *)vhead_realvp },
        { VOPNAME_GETPAGE, (femop_t *)vhead_getpage },
        { VOPNAME_PUTPAGE, (femop_t *)vhead_putpage },
        { VOPNAME_MAP, (femop_t *)vhead_map },
        { VOPNAME_ADDMAP, (femop_t *)vhead_addmap },
        { VOPNAME_DELMAP, (femop_t *)vhead_delmap },
        { VOPNAME_POLL, (femop_t *)vhead_poll },
        { VOPNAME_DUMP, (femop_t *)vhead_dump },
        { VOPNAME_PATHCONF, (femop_t *)vhead_pathconf },
        { VOPNAME_PAGEIO, (femop_t *)vhead_pageio },
        { VOPNAME_DUMPCTL, (femop_t *)vhead_dumpctl },
        { VOPNAME_DISPOSE, (femop_t *)vhead_dispose },
        { VOPNAME_SETSECATTR, (femop_t *)vhead_setsecattr },
        { VOPNAME_GETSECATTR, (femop_t *)vhead_getsecattr },
        { VOPNAME_SHRLOCK, (femop_t *)vhead_shrlock },
        { VOPNAME_VNEVENT, (femop_t *)vhead_vnevent },
        { VOPNAME_REQZCBUF, (femop_t *)vhead_reqzcbuf },
        { VOPNAME_RETZCBUF, (femop_t *)vhead_retzcbuf },
        {       NULL,   NULL    }
};

/*
 * specification table for the vfshead vnode operations.
 * It is an error for any operations to be missing.
 */

static struct fs_operation_def fshead_vfs_spec[]  = {
        { VFSNAME_MOUNT, (femop_t *)fshead_mount },
        { VFSNAME_UNMOUNT, (femop_t *)fshead_unmount },
        { VFSNAME_ROOT, (femop_t *)fshead_root },
        { VFSNAME_STATVFS, (femop_t *)fshead_statvfs },
        { VFSNAME_SYNC, (femop_t *)fshead_sync },
        { VFSNAME_VGET, (femop_t *)fshead_vget },
        { VFSNAME_MOUNTROOT, (femop_t *)fshead_mountroot },
        { VFSNAME_FREEVFS, (femop_t *)fshead_freevfs },
        { VFSNAME_VNSTATE, (femop_t *)fshead_vnstate },
        { VFSNAME_SYNCFS, (femop_t *)fshead_syncfs },
        {       NULL,   NULL    }
};

/*
 * This set of routines transfer control to the next stacked monitor.
 *
 * Each routine is identical except for naming, types and arguments.
 *
 * The basic steps are:
 * 1.  Decrease the stack pointer by one.
 * 2.  If the current item is a base operation (vnode, vfs), goto 5.
 * 3.  If the current item does not have a corresponding operation, goto 1
 * 4.  Return by invoking the current item with the argument handle.
 * 5.  Return by invoking the base operation with the base object.
 *
 * for each classification, there needs to be at least one "next" operation
 * for each "head"operation.
 *
 */

int
vnext_open(femarg_t *vf, int mode, cred_t *cr, caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_open, femop_open);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, mode, cr, ct));
}

int
vnext_close(femarg_t *vf, int flag, int count, offset_t offset, cred_t *cr,
    caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_close, femop_close);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, flag, count, offset, cr, ct));
}

int
vnext_read(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr,
    caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_read, femop_read);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, uiop, ioflag, cr, ct));
}

int
vnext_write(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr,
    caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_write, femop_write);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, uiop, ioflag, cr, ct));
}

int
vnext_ioctl(femarg_t *vf, int cmd, intptr_t arg, int flag, cred_t *cr,
    int *rvalp, caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_ioctl, femop_ioctl);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, cmd, arg, flag, cr, rvalp, ct));
}

int
vnext_setfl(femarg_t *vf, int oflags, int nflags, cred_t *cr,
    caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_setfl, femop_setfl);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, oflags, nflags, cr, ct));
}

int
vnext_getattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
    caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_getattr, femop_getattr);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, vap, flags, cr, ct));
}

int
vnext_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
    caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_setattr, femop_setattr);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, vap, flags, cr, ct));
}

int
vnext_access(femarg_t *vf, int mode, int flags, cred_t *cr,
    caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_access, femop_access);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, mode, flags, cr, ct));
}

int
vnext_lookup(femarg_t *vf, char *nm, vnode_t **vpp, pathname_t *pnp,
    int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
    int *direntflags, pathname_t *realpnp)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_lookup, femop_lookup);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, nm, vpp, pnp, flags, rdir, cr, ct,
            direntflags, realpnp));
}

int
vnext_create(femarg_t *vf, char *name, vattr_t *vap, vcexcl_t excl,
    int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct,
    vsecattr_t *vsecp)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_create, femop_create);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, name, vap, excl, mode, vpp, cr, flag, ct, vsecp));
}

int
vnext_remove(femarg_t *vf, char *nm, cred_t *cr, caller_context_t *ct,
    int flags)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_remove, femop_remove);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, nm, cr, ct, flags));
}

int
vnext_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr,
    caller_context_t *ct, int flags)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_link, femop_link);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, svp, tnm, cr, ct, flags));
}

int
vnext_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr,
    caller_context_t *ct, int flags)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_rename, femop_rename);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, snm, tdvp, tnm, cr, ct, flags));
}

int
vnext_mkdir(femarg_t *vf, char *dirname, vattr_t *vap, vnode_t **vpp,
    cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_mkdir, femop_mkdir);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, dirname, vap, vpp, cr, ct, flags, vsecp));
}

int
vnext_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr,
    caller_context_t *ct, int flags)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_rmdir, femop_rmdir);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, nm, cdir, cr, ct, flags));
}

int
vnext_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp,
    caller_context_t *ct, int flags)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_readdir, femop_readdir);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, uiop, cr, eofp, ct, flags));
}

int
vnext_symlink(femarg_t *vf, char *linkname, vattr_t *vap, char *target,
    cred_t *cr, caller_context_t *ct, int flags)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_symlink, femop_symlink);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, linkname, vap, target, cr, ct, flags));
}

int
vnext_readlink(femarg_t *vf, uio_t *uiop, cred_t *cr, caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_readlink, femop_readlink);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, uiop, cr, ct));
}

int
vnext_fsync(femarg_t *vf, int syncflag, cred_t *cr, caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_fsync, femop_fsync);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, syncflag, cr, ct));
}

void
vnext_inactive(femarg_t *vf, cred_t *cr, caller_context_t *ct)
{
        void (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, void, &arg0, vop_inactive, femop_inactive);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        (*func)(arg0, cr, ct);
}

int
vnext_fid(femarg_t *vf, fid_t *fidp, caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_fid, femop_fid);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, fidp, ct));
}

int
vnext_rwlock(femarg_t *vf, int write_lock, caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_rwlock, femop_rwlock);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, write_lock, ct));
}

void
vnext_rwunlock(femarg_t *vf, int write_lock, caller_context_t *ct)
{
        void (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, void, &arg0, vop_rwunlock, femop_rwunlock);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        (*func)(arg0, write_lock, ct);
}

int
vnext_seek(femarg_t *vf, offset_t ooff, offset_t *noffp, caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_seek, femop_seek);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, ooff, noffp, ct));
}

int
vnext_cmp(femarg_t *vf, vnode_t *vp2, caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_cmp, femop_cmp);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, vp2, ct));
}

int
vnext_frlock(femarg_t *vf, int cmd, struct flock64 *bfp, int flag,
    offset_t offset, struct flk_callback *flk_cbp, cred_t *cr,
    caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_frlock, femop_frlock);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, cmd, bfp, flag, offset, flk_cbp, cr, ct));
}

int
vnext_space(femarg_t *vf, int cmd, struct flock64 *bfp, int flag,
    offset_t offset, cred_t *cr, caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_space, femop_space);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, cmd, bfp, flag, offset, cr, ct));
}

int
vnext_realvp(femarg_t *vf, vnode_t **vpp, caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_realvp, femop_realvp);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, vpp, ct));
}

int
vnext_getpage(femarg_t *vf, offset_t off, size_t len, uint_t *protp,
    struct page **plarr, size_t plsz, struct seg *seg, caddr_t addr,
    enum seg_rw rw, cred_t *cr, caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_getpage, femop_getpage);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, off, len, protp, plarr, plsz, seg, addr, rw,
            cr, ct));
}

int
vnext_putpage(femarg_t *vf, offset_t off, size_t len, int flags,
    cred_t *cr, caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_putpage, femop_putpage);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, off, len, flags, cr, ct));
}

int
vnext_map(femarg_t *vf, offset_t off, struct as *as, caddr_t *addrp,
    size_t len, uchar_t prot, uchar_t maxprot, uint_t flags,
    cred_t *cr, caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_map, femop_map);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, off, as, addrp, len, prot, maxprot, flags,
            cr, ct));
}

int
vnext_addmap(femarg_t *vf, offset_t off, struct as *as, caddr_t addr,
    size_t len, uchar_t prot, uchar_t maxprot, uint_t flags,
    cred_t *cr, caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_addmap, femop_addmap);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, off, as, addr, len, prot, maxprot, flags,
            cr, ct));
}

int
vnext_delmap(femarg_t *vf, offset_t off, struct as *as, caddr_t addr,
    size_t len, uint_t prot, uint_t maxprot, uint_t flags,
    cred_t *cr, caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_delmap, femop_delmap);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, off, as, addr, len, prot, maxprot, flags,
            cr, ct));
}

int
vnext_poll(femarg_t *vf, short events, int anyyet, short *reventsp,
    struct pollhead **phpp, caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_poll, femop_poll);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, events, anyyet, reventsp, phpp, ct));
}

int
vnext_dump(femarg_t *vf, caddr_t addr, offset_t lbdn, offset_t dblks,
    caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_dump, femop_dump);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, addr, lbdn, dblks, ct));
}

int
vnext_pathconf(femarg_t *vf, int cmd, ulong_t *valp, cred_t *cr,
    caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_pathconf, femop_pathconf);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, cmd, valp, cr, ct));
}

int
vnext_pageio(femarg_t *vf, struct page *pp, u_offset_t io_off,
    size_t io_len, int flags, cred_t *cr, caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_pageio, femop_pageio);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, pp, io_off, io_len, flags, cr, ct));
}

int
vnext_dumpctl(femarg_t *vf, int action, offset_t *blkp, caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_dumpctl, femop_dumpctl);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, action, blkp, ct));
}

void
vnext_dispose(femarg_t *vf, struct page *pp, int flag, int dn, cred_t *cr,
    caller_context_t *ct)
{
        void (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, void, &arg0, vop_dispose, femop_dispose);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        (*func)(arg0, pp, flag, dn, cr, ct);
}

int
vnext_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flag, cred_t *cr,
    caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_setsecattr, femop_setsecattr);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, vsap, flag, cr, ct));
}

int
vnext_getsecattr(femarg_t *vf, vsecattr_t *vsap, int flag, cred_t *cr,
    caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_getsecattr, femop_getsecattr);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, vsap, flag, cr, ct));
}

int
vnext_shrlock(femarg_t *vf, int cmd, struct shrlock *shr, int flag,
    cred_t *cr, caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_shrlock, femop_shrlock);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, cmd, shr, flag, cr, ct));
}

int
vnext_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp, char *cname,
    caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_vnevent, femop_vnevent);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, vnevent, dvp, cname, ct));
}

int
vnext_reqzcbuf(femarg_t *vf, enum uio_rw ioflag, xuio_t *xuiop, cred_t *cr,
    caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_reqzcbuf, femop_reqzcbuf);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, ioflag, xuiop, cr, ct));
}

int
vnext_retzcbuf(femarg_t *vf, xuio_t *xuiop, cred_t *cr, caller_context_t *ct)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vsop_find(vf, &func, int, &arg0, vop_retzcbuf, femop_retzcbuf);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, xuiop, cr, ct));
}

int
vfsnext_mount(fsemarg_t *vf, vnode_t *mvp, struct mounta *uap, cred_t *cr)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vfsop_find(vf, &func, int, &arg0, vfs_mount, fsemop_mount);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, mvp, uap, cr));
}

int
vfsnext_unmount(fsemarg_t *vf, int flag, cred_t *cr)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vfsop_find(vf, &func, int, &arg0, vfs_unmount, fsemop_unmount);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, flag, cr));
}

int
vfsnext_root(fsemarg_t *vf, vnode_t **vpp)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vfsop_find(vf, &func, int, &arg0, vfs_root, fsemop_root);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, vpp));
}

int
vfsnext_statvfs(fsemarg_t *vf, statvfs64_t *sp)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vfsop_find(vf, &func, int, &arg0, vfs_statvfs, fsemop_statvfs);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, sp));
}

int
vfsnext_sync(fsemarg_t *vf, short flag, cred_t *cr)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vfsop_find(vf, &func, int, &arg0, vfs_sync, fsemop_sync);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, flag, cr));
}

int
vfsnext_vget(fsemarg_t *vf, vnode_t **vpp, fid_t *fidp)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vfsop_find(vf, &func, int, &arg0, vfs_vget, fsemop_vget);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, vpp, fidp));
}

int
vfsnext_mountroot(fsemarg_t *vf, enum whymountroot reason)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vfsop_find(vf, &func, int, &arg0, vfs_mountroot, fsemop_mountroot);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, reason));
}

void
vfsnext_freevfs(fsemarg_t *vf)
{
        void (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vfsop_find(vf, &func, void, &arg0, vfs_freevfs, fsemop_freevfs);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        (*func)(arg0);
}

int
vfsnext_vnstate(fsemarg_t *vf, vnode_t *vp, vntrans_t nstate)
{
        int (*func)() = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vfsop_find(vf, &func, int, &arg0, vfs_vnstate, fsemop_vnstate);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, vp, nstate));
}

int
vfsnext_syncfs(fsemarg_t *vf, uint64_t flags, cred_t *cr)
{
        int (*func)(vfs_t *, uint64_t, cred_t *) = NULL;
        void *arg0 = NULL;

        ASSERT(vf != NULL);
        vf->fa_fnode--;
        vfsop_find(vf, &func, int, &arg0, vfs_syncfs, fsemop_syncfs);
        ASSERT(func != NULL);
        ASSERT(arg0 != NULL);
        return ((*func)(arg0, flags, cr));
}

/*
 * Create a new fem_head and associate with the vnode.
 * To keep the unaugmented vnode access path lock free, we spin
 * update this - create a new one, then try and install it. If
 * we fail to install, release the old one and pretend we succeeded.
 */

static struct fem_head *
new_femhead(struct fem_head **hp)
{
        struct fem_head *head;

        head = kmem_alloc(sizeof (*head), KM_SLEEP);
        mutex_init(&head->femh_lock, NULL, MUTEX_DEFAULT, NULL);
        head->femh_list = NULL;
        if (atomic_cas_ptr(hp, NULL, head) != NULL) {
                kmem_free(head, sizeof (*head));
                head = *hp;
        }
        return (head);
}

/*
 * Create a fem_list.  The fem_list that gets returned is in a
 * very rudimentary state and MUST NOT be used until it's initialized
 * (usually by femlist_construct() or fem_dup_list()).  The refcount
 * and size is set properly and top-of-stack is set to the "guard" node
 * just to be consistent.
 *
 * If anyone were to accidentally trying to run on this fem_list before
 * it's initialized then the system would likely panic trying to defererence
 * the (NULL) fn_op pointer.
 *
 */
static struct fem_list *
femlist_create(int numnodes)
{
        struct fem_list *sp;

        sp = kmem_alloc(fl_ntob(numnodes), KM_SLEEP);
        sp->feml_refc  = 1;
        sp->feml_ssize = numnodes;
        sp->feml_nodes[0] = FEM_GUARD(FEMTYPE_NULL);
        sp->feml_tos = 0;
        return (sp);
}

/*
 * Construct a new femlist.
 * The list is constructed with the appropriate type of guard to
 * anchor it, and inserts the original ops.
 */

static struct fem_list *
femlist_construct(void *baseops, int type, int numnodes)
{
        struct fem_list *sp;

        sp = femlist_create(numnodes);
        sp->feml_nodes[0] = FEM_GUARD(type);
        sp->feml_nodes[1].fn_op.anon = baseops;
        sp->feml_nodes[1].fn_available = NULL;
        sp->feml_nodes[1].fn_av_hold = NULL;
        sp->feml_nodes[1].fn_av_rele = NULL;
        sp->feml_tos = 1;
        return (sp);
}

/*
 * Duplicate a list.  Copy the original list to the clone.
 *
 * NOTE: The caller must have the fem_head for the lists locked.
 * Assuming the appropriate lock is held and the caller has done the
 * math right, the clone list should be big enough to old the original.
 */

static void
fem_dup_list(struct fem_list *orig, struct fem_list *clone)
{
        int             i;

        ASSERT(clone->feml_ssize >= orig->feml_ssize);

        bcopy(orig->feml_nodes, clone->feml_nodes,
            sizeof (orig->feml_nodes[0]) * orig->feml_ssize);
        clone->feml_tos = orig->feml_tos;
        /*
         * Now that we've copied the old list (orig) to the new list (clone),
         * we need to walk the new list and put another hold on fn_available.
         */
        for (i = clone->feml_tos; i > 0; i--) {
                struct fem_node *fnp = &clone->feml_nodes[i];

                if (fnp->fn_av_hold)
                        (*(fnp->fn_av_hold))(fnp->fn_available);
        }
}


static int
fem_push_node(
    struct fem_head **hp,
    void **baseops,
    int type,
    struct fem_node *nnode,
    femhow_t how)
{
        struct fem_head *hd;
        struct fem_list *list;
        void            *oldops;
        int             retry;
        int             error = 0;
        int             i;

        /* Validate the node */
        if ((nnode->fn_op.anon == NULL) || (nnode->fn_available == NULL)) {
                return (EINVAL);
        }

        if ((hd = *hp) == NULL) { /* construct a proto-list */
                hd = new_femhead(hp);
        }
        /*
         * RULE: once a femhead has been pushed onto a object, it cannot be
         * removed until the object is destroyed.  It can be deactivated by
         * placing the original 'object operations' onto the object, which
         * will ignore the femhead.
         * The loop will exist when the femh_list has space to push a monitor
         * onto it.
         */
        do {
                retry = 1;
                list = fem_lock(hd);
                oldops = *baseops;

                if (list != NULL) {
                        if (list->feml_tos+1 < list->feml_ssize) {
                                retry = 0;
                        } else {
                                struct fem_list *olist = list;

                                fem_addref(olist);
                                fem_unlock(hd);
                                list = femlist_create(olist->feml_ssize * 2);
                                (void) fem_lock(hd);
                                if (hd->femh_list == olist) {
                                        if (list->feml_ssize <=
                                            olist->feml_ssize) {
                                                /*
                                                 * We have a new list, but it
                                                 * is too small to hold the
                                                 * original contents plus the
                                                 * one to push.  Release the
                                                 * new list and start over.
                                                 */
                                                fem_release(list);
                                                fem_unlock(hd);
                                        } else {
                                                /*
                                                 * Life is good:  Our new list
                                                 * is big enough to hold the
                                                 * original list (olist) + 1.
                                                 */
                                                fem_dup_list(olist, list);
                                                /* orphan this list */
                                                hd->femh_list = list;
                                                (void) fem_delref(olist);
                                                retry = 0;
                                        }
                                } else {
                                        /* concurrent update, retry */
                                        fem_release(list);
                                        fem_unlock(hd);
                                }
                                /* remove the reference we added above */
                                fem_release(olist);
                        }
                } else {
                        fem_unlock(hd);
                        list = femlist_construct(oldops, type, NNODES_DEFAULT);
                        (void) fem_lock(hd);
                        if (hd->femh_list != NULL || *baseops != oldops) {
                                /* concurrent update, retry */
                                fem_release(list);
                                fem_unlock(hd);
                        } else {
                                hd->femh_list = list;
                                *baseops = FEM_HEAD(type);
                                retry = 0;
                        }
                }
        } while (retry);

        ASSERT(mutex_owner(&hd->femh_lock) == curthread);
        ASSERT(list->feml_tos+1 < list->feml_ssize);

        /*
         * The presence of "how" will modify the behavior of how/if
         * nodes are pushed.  If it's FORCE, then we can skip
         * all the checks and push it on.
         */
        if (how != FORCE) {
                /* Start at the top and work our way down */
                for (i = list->feml_tos; i > 0; i--) {
                        void *fn_av = list->feml_nodes[i].fn_available;
                        void *fn_op = list->feml_nodes[i].fn_op.anon;

                        /*
                         * OPARGUNIQ means that this node should not
                         * be pushed on if a node with the same op/avail
                         * combination exists.  This situation returns
                         * EBUSY.
                         *
                         * OPUNIQ means that this node should not be
                         * pushed on if a node with the same op exists.
                         * This situation also returns EBUSY.
                         */
                        switch (how) {

                        case OPUNIQ:
                                if (fn_op == nnode->fn_op.anon) {
                                        error = EBUSY;
                                }
                                break;

                        case OPARGUNIQ:
                                if ((fn_op == nnode->fn_op.anon) &&
                                    (fn_av == nnode->fn_available)) {
                                        error = EBUSY;
                                }
                                break;

                        default:
                                error = EINVAL; /* Unexpected value */
                                break;
                        }

                        if (error)
                                break;
                }
        }

        if (error == 0) {
                /*
                 * If no errors, slap the node on the list.
                 * Note: The following is a structure copy.
                 */
                list->feml_nodes[++(list->feml_tos)] = *nnode;
        }

        fem_unlock(hd);
        return (error);
}

/*
 * Remove a node by copying the list above it down a notch.
 * If the list is busy, replace it with an idle one and work
 * upon it.
 * A node matches if the opset matches and the datap matches or is
 * null.
 */

static int
remove_node(struct fem_list *sp, void **baseops, void *opset, void *datap)
{
        int     i;
        struct fem_node *fn;

        fn = NULL;
        for (i = sp->feml_tos; i > 0; i--) {
                fn = sp->feml_nodes + i;
                if (fn->fn_op.anon == opset &&
                    (fn->fn_available == datap || datap == NULL)) {
                        break;
                }
        }
        if (i == 0) {
                return (EINVAL);
        }

        /*
         * At this point we have a node in-hand (*fn) that we are about
         * to remove by overwriting it and adjusting the stack.  This is
         * our last chance to do anything with this node so we do the
         * release on the arg.
         */
        if (fn->fn_av_rele)
                (*(fn->fn_av_rele))(fn->fn_available);

        while (i++ < sp->feml_tos) {
                sp->feml_nodes[i-1] = sp->feml_nodes[i];
        }
        if (--(sp->feml_tos) == 1) { /* Empty, restore ops */
                *baseops = sp->feml_nodes[1].fn_op.anon;
        }
        return (0);
}

static int
fem_remove_node(struct fem_head *fh, void **baseops, void *opset, void *datap)
{
        struct fem_list *sp;
        int             error = 0;
        int             retry;

        if (fh == NULL) {
                return (EINVAL);
        }

        do {
                retry = 0;
                if ((sp = fem_lock(fh)) == NULL) {
                        fem_unlock(fh);
                        error = EINVAL;
                } else if (sp->feml_refc == 1) {
                        error = remove_node(sp, baseops, opset, datap);
                        if (sp->feml_tos == 1) {
                                /*
                                 * The top-of-stack was decremented by
                                 * remove_node().  If it got down to 1,
                                 * then the base ops were replaced and we
                                 * call fem_release() which will free the
                                 * fem_list.
                                 */
                                fem_release(sp);
                                fh->femh_list = NULL;
                                /* XXX - Do we need a membar_producer() call? */
                        }
                        fem_unlock(fh);
                } else {
                        /* busy - install a new one without this monitor */
                        struct fem_list *nsp;   /* New fem_list being cloned */

                        fem_addref(sp);
                        fem_unlock(fh);
                        nsp = femlist_create(sp->feml_ssize);
                        if (fem_lock(fh) == sp) {
                                /*
                                 * We popped out of the lock, created a
                                 * list, then relocked.  If we're in here
                                 * then the fem_head points to the same list
                                 * it started with.
                                 */
                                fem_dup_list(sp, nsp);
                                error = remove_node(nsp, baseops, opset, datap);
                                if (error != 0) {
                                        fem_release(nsp);
                                } else if (nsp->feml_tos == 1) {
                                        /* New list now empty, tear it down */
                                        fem_release(nsp);
                                        fh->femh_list = NULL;
                                } else {
                                        fh->femh_list = nsp;
                                }
                                (void) fem_delref(sp);
                        } else {
                                /* List changed while locked, try again... */
                                fem_release(nsp);
                                retry = 1;
                        }
                        /*
                         * If error is set, then we tried to remove a node
                         * from the list, but failed.  This means that we
                         * will still be using this list so don't release it.
                         */
                        if (error == 0)
                                fem_release(sp);
                        fem_unlock(fh);
                }
        } while (retry);
        return (error);
}


/*
 * perform operation on each element until one returns non zero
 */
static int
fem_walk_list(
    struct fem_list *sp,
    int (*f)(struct fem_node *, void *, void *),
    void *mon,
    void *arg)
{
        int     i;

        ASSERT(sp != NULL);
        for (i = sp->feml_tos; i > 0; i--) {
                if ((*f)(sp->feml_nodes+i, mon, arg) != 0) {
                        break;
                }
        }
        return (i);
}

/*
 * companion comparison functions.
 */
static int
fem_compare_mon(struct fem_node *n, void *mon, void *arg)
{
        return ((n->fn_op.anon == mon) && (n->fn_available == arg));
}

/*
 * VNODE interposition.
 */

int
fem_create(char *name, const struct fs_operation_def *templ,
    fem_t **actual)
{
        int     unused_ops = 0;
        int     e;
        fem_t   *newf;

        newf = fem_alloc();
        newf->name = name;
        newf->templ = templ;

        e =  fs_build_vector(newf, &unused_ops, fem_opdef, templ);
        if (e != 0) {
#ifdef DEBUG
                cmn_err(CE_WARN, "fem_create: error %d building vector", e);
#endif
                fem_free(newf);
        } else {
                *actual = newf;
        }
        return (e);
}

int
fem_install(
    vnode_t *vp,                /* Vnode on which monitor is being installed */
    fem_t *mon,                 /* Monitor operations being installed */
    void *arg,                  /* Opaque data used by monitor */
    femhow_t how,               /* Installation control */
    void (*arg_hold)(void *),   /* Hold routine for "arg" */
    void (*arg_rele)(void *))   /* Release routine for "arg" */
{
        int     error;
        struct fem_node nnode;

        nnode.fn_available = arg;
        nnode.fn_op.fem = mon;
        nnode.fn_av_hold = arg_hold;
        nnode.fn_av_rele = arg_rele;
        /*
         * If we have a non-NULL hold function, do the hold right away.
         * The release is done in remove_node().
         */
        if (arg_hold)
                (*arg_hold)(arg);

        error = fem_push_node(&vp->v_femhead, (void **)&vp->v_op, FEMTYPE_VNODE,
            &nnode, how);

        /* If there was an error then the monitor wasn't pushed */
        if (error && arg_rele)
                (*arg_rele)(arg);

        return (error);
}

int
fem_is_installed(vnode_t *v, fem_t *mon, void *arg)
{
        int     e;
        struct fem_list *fl;

        fl = fem_get(v->v_femhead);
        if (fl != NULL) {
                e = fem_walk_list(fl, fem_compare_mon, (void *)mon, arg);
                fem_release(fl);
                return (e);
        }
        return (0);
}

int
fem_uninstall(vnode_t *v, fem_t *mon, void *arg)
{
        int     e;
        e = fem_remove_node(v->v_femhead, (void **)&v->v_op,
            (void *)mon, arg);
        return (e);
}

void
fem_setvnops(vnode_t *v, vnodeops_t *newops)
{
        vnodeops_t      *r;

        ASSERT(v != NULL);
        ASSERT(newops != NULL);

        do {
                r = v->v_op;
                membar_consumer();
                if (v->v_femhead != NULL) {
                        struct fem_list *fl;
                        if ((fl = fem_lock(v->v_femhead)) != NULL) {
                                fl->feml_nodes[1].fn_op.vnode = newops;
                                fem_unlock(v->v_femhead);
                                return;
                        }
                        fem_unlock(v->v_femhead);
                }
        } while (atomic_cas_ptr(&v->v_op, r, newops) != r);
}

vnodeops_t *
fem_getvnops(vnode_t *v)
{
        vnodeops_t      *r;

        ASSERT(v != NULL);

        r = v->v_op;
        membar_consumer();
        if (v->v_femhead != NULL) {
                struct fem_list *fl;
                if ((fl = fem_lock(v->v_femhead)) != NULL) {
                        r = fl->feml_nodes[1].fn_op.vnode;
                }
                fem_unlock(v->v_femhead);
        }
        return (r);
}


/*
 * VFS interposition
 */
int
fsem_create(char *name, const struct fs_operation_def *templ,
    fsem_t **actual)
{
        int     unused_ops = 0;
        int     e;
        fsem_t  *newv;

        newv = fsem_alloc();
        newv->name = (const char *)name;
        newv->templ = templ;

        e = fs_build_vector(newv, &unused_ops, fsem_opdef, templ);
        if (e != 0) {
#ifdef DEBUG
                cmn_err(CE_WARN, "fsem_create: error %d building vector", e);
#endif
                fsem_free(newv);
        } else {
                *actual = newv;
        }
        return (e);
}

/*
 * These need to be re-written, but there should be more common bits.
 */

int
fsem_is_installed(struct vfs *v, fsem_t *mon, void *arg)
{
        struct fem_list *fl;

        if (v->vfs_implp == NULL)
                return (0);

        fl = fem_get(v->vfs_femhead);
        if (fl != NULL) {
                int     e;
                e = fem_walk_list(fl, fem_compare_mon, (void *)mon, arg);
                fem_release(fl);
                return (e);
        }
        return (0);
}

int
fsem_install(
    struct vfs *vfsp,           /* VFS on which monitor is being installed */
    fsem_t *mon,                /* Monitor operations being installed */
    void *arg,                  /* Opaque data used by monitor */
    femhow_t how,               /* Installation control */
    void (*arg_hold)(void *),   /* Hold routine for "arg" */
    void (*arg_rele)(void *))   /* Release routine for "arg" */
{
        int     error;
        struct fem_node nnode;

        /* If this vfs hasn't been properly initialized, fail the install */
        if (vfsp->vfs_implp == NULL)
                return (EINVAL);

        nnode.fn_available = arg;
        nnode.fn_op.fsem = mon;
        nnode.fn_av_hold = arg_hold;
        nnode.fn_av_rele = arg_rele;
        /*
         * If we have a non-NULL hold function, do the hold right away.
         * The release is done in remove_node().
         */
        if (arg_hold)
                (*arg_hold)(arg);

        error = fem_push_node(&vfsp->vfs_femhead, (void **)&vfsp->vfs_op,
            FEMTYPE_VFS, &nnode, how);

        /* If there was an error then the monitor wasn't pushed */
        if (error && arg_rele)
                (*arg_rele)(arg);

        return (error);
}

int
fsem_uninstall(struct vfs *v, fsem_t *mon, void *arg)
{
        int     e;

        if (v->vfs_implp == NULL)
                return (EINVAL);

        e = fem_remove_node(v->vfs_femhead, (void **)&v->vfs_op,
            (void *)mon, arg);
        return (e);
}

void
fsem_setvfsops(vfs_t *v, vfsops_t *newops)
{
        vfsops_t        *r;

        ASSERT(v != NULL);
        ASSERT(newops != NULL);
        ASSERT(v->vfs_implp);

        do {
                r = v->vfs_op;
                membar_consumer();
                if (v->vfs_femhead != NULL) {
                        struct fem_list *fl;
                        if ((fl = fem_lock(v->vfs_femhead)) != NULL) {
                                fl->feml_nodes[1].fn_op.vfs = newops;
                                fem_unlock(v->vfs_femhead);
                                return;
                        }
                        fem_unlock(v->vfs_femhead);
                }
        } while (atomic_cas_ptr(&v->vfs_op, r, newops) != r);
}

vfsops_t *
fsem_getvfsops(vfs_t *v)
{
        vfsops_t        *r;

        ASSERT(v != NULL);
        ASSERT(v->vfs_implp);

        r = v->vfs_op;
        membar_consumer();
        if (v->vfs_femhead != NULL) {
                struct fem_list *fl;
                if ((fl = fem_lock(v->vfs_femhead)) != NULL) {
                        r = fl->feml_nodes[1].fn_op.vfs;
                }
                fem_unlock(v->vfs_femhead);
        }
        return (r);
}

/*
 * Setup FEM.
 */
void
fem_init()
{
        struct fem_type_info   *fi;

        /*
         * This femtype is only used for fem_list creation so we only
         * need the "guard" to be initialized so that feml_tos has
         * some rudimentary meaning.  A fem_list must not be used until
         * it has been initialized (either via femlist_construct() or
         * fem_dup_list()).  Anything that tries to use this fem_list
         * before it's actually initialized would panic the system as
         * soon as "fn_op" (NULL) is dereferenced.
         */
        fi = femtype + FEMTYPE_NULL;
        fi->errf = fem_err;
        fi->guard.fn_available = (void *)&fi->guard;
        fi->guard.fn_av_hold = NULL;
        fi->guard.fn_av_rele = NULL;
        fi->guard.fn_op.anon = NULL;

        fi = femtype + FEMTYPE_VNODE;
        fi->errf = fem_err;
        fi->head.fn_available = NULL;
        fi->head.fn_av_hold = NULL;
        fi->head.fn_av_rele = NULL;
        (void) vn_make_ops("fem-head", fhead_vn_spec, &fi->head.fn_op.vnode);
        fi->guard.fn_available = (void *)&fi->guard;
        fi->guard.fn_av_hold = NULL;
        fi->guard.fn_av_rele = NULL;
        (void) fem_create("fem-guard", fem_guard_ops, &fi->guard.fn_op.fem);

        fi = femtype + FEMTYPE_VFS;
        fi->errf = fsem_err;
        fi->head.fn_available = NULL;
        fi->head.fn_av_hold = NULL;
        fi->head.fn_av_rele = NULL;
        (void) vfs_makefsops(fshead_vfs_spec, &fi->head.fn_op.vfs);

        fi->guard.fn_available = (void *)&fi->guard;
        fi->guard.fn_av_hold = NULL;
        fi->guard.fn_av_rele = NULL;
        (void) fsem_create("fem-guard", fsem_guard_ops, &fi->guard.fn_op.fsem);
}


int
fem_err()
{
        cmn_err(CE_PANIC, "fem/vnode operations corrupt");
        return (0);
}

int
fsem_err()
{
        cmn_err(CE_PANIC, "fem/vfs operations corrupt");
        return (0);
}