root/sys/ufs/mfs/mfs_vnops.c
/*      $NetBSD: mfs_vnops.c,v 1.64 2022/03/19 13:48:42 hannken Exp $   */

/*
 * Copyright (c) 1989, 1993
 *      The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *      @(#)mfs_vnops.c 8.11 (Berkeley) 5/22/95
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: mfs_vnops.c,v 1.64 2022/03/19 13:48:42 hannken Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/bufq.h>
#include <sys/vnode.h>
#include <sys/kmem.h>

#include <miscfs/genfs/genfs.h>
#include <miscfs/specfs/specdev.h>

#include <machine/vmparam.h>

#include <ufs/mfs/mfsnode.h>
#include <ufs/mfs/mfs_extern.h>

/*
 * mfs vnode operations.
 */
int (**mfs_vnodeop_p)(void *);
const struct vnodeopv_entry_desc mfs_vnodeop_entries[] = {
        { &vop_default_desc, vn_default_error },
        { &vop_parsepath_desc, genfs_parsepath },       /* parsepath */
        { &vop_lookup_desc, genfs_badop },              /* lookup */
        { &vop_create_desc, genfs_badop },              /* create */
        { &vop_mknod_desc, genfs_badop },               /* mknod */
        { &vop_open_desc, mfs_open },                   /* open */
        { &vop_close_desc, mfs_close },                 /* close */
        { &vop_access_desc, genfs_badop },              /* access */
        { &vop_accessx_desc, genfs_badop },             /* accessx */
        { &vop_getattr_desc, genfs_badop },             /* getattr */
        { &vop_setattr_desc, genfs_badop },             /* setattr */
        { &vop_read_desc, genfs_badop },                /* read */
        { &vop_write_desc, genfs_badop },               /* write */
        { &vop_fallocate_desc, genfs_eopnotsupp },      /* fallocate */
        { &vop_fdiscard_desc, genfs_eopnotsupp },       /* fdiscard */
        { &vop_ioctl_desc, genfs_enoioctl },            /* ioctl */
        { &vop_poll_desc, genfs_badop },                /* poll */
        { &vop_revoke_desc, genfs_revoke },             /* revoke */
        { &vop_mmap_desc, genfs_badop },                /* mmap */
        { &vop_fsync_desc, spec_fsync },                /* fsync */
        { &vop_seek_desc, genfs_badop },                /* seek */
        { &vop_remove_desc, genfs_badop },              /* remove */
        { &vop_link_desc, genfs_badop },                /* link */
        { &vop_rename_desc, genfs_badop },              /* rename */
        { &vop_mkdir_desc, genfs_badop },               /* mkdir */
        { &vop_rmdir_desc, genfs_badop },               /* rmdir */
        { &vop_symlink_desc, genfs_badop },             /* symlink */
        { &vop_readdir_desc, genfs_badop },             /* readdir */
        { &vop_readlink_desc, genfs_badop },            /* readlink */
        { &vop_abortop_desc, genfs_badop },             /* abortop */
        { &vop_inactive_desc, mfs_inactive },           /* inactive */
        { &vop_reclaim_desc, mfs_reclaim },             /* reclaim */
        { &vop_lock_desc, genfs_lock },                 /* lock */
        { &vop_unlock_desc, genfs_unlock },             /* unlock */
        { &vop_bmap_desc, mfs_bmap },                   /* bmap */
        { &vop_strategy_desc, mfs_strategy },           /* strategy */
        { &vop_print_desc, mfs_print },                 /* print */
        { &vop_islocked_desc, genfs_islocked },         /* islocked */
        { &vop_pathconf_desc, genfs_badop },            /* pathconf */
        { &vop_advlock_desc, genfs_badop },             /* advlock */
        { &vop_bwrite_desc, vn_bwrite },                /* bwrite */
        { &vop_putpages_desc, genfs_null_putpages },    /* putpages */
        { NULL, NULL }
};
const struct vnodeopv_desc mfs_vnodeop_opv_desc =
        { &mfs_vnodeop_p, mfs_vnodeop_entries };

/*
 * Vnode Operations.
 *
 * Open called to allow memory filesystem to initialize and
 * validate before actual IO. Record our process identifier
 * so we can tell when we are doing I/O to ourself.
 */
/* ARGSUSED */
int
mfs_open(void *v)
{
        struct vop_open_args /* {
                struct vnode *a_vp;
                int  a_mode;
                kauth_cred_t a_cred;
        } */ *ap = v;

        if (ap->a_vp->v_type != VBLK) {
                panic("mfs_open not VBLK");
                /* NOTREACHED */
        }
        return (0);
}

/*
 * Pass I/O requests to the memory filesystem process.
 */
int
mfs_strategy(void *v)
{
        struct vop_strategy_args /* {
                struct vnode *a_vp;
                struct buf *a_bp;
        } */ *ap = v;
        struct vnode *vp = ap->a_vp;
        struct buf *bp = ap->a_bp;
        struct mfsnode *mfsp;

        if (vp->v_type != VBLK || vrefcnt(vp) == 0)
                panic("mfs_strategy: bad dev");
        mfsp = VTOMFS(vp);
        /* check for mini-root access */
        if (mfsp->mfs_proc == NULL) {
                void *base;

                base = (char *)mfsp->mfs_baseoff + (bp->b_blkno << DEV_BSHIFT);
                if (bp->b_flags & B_READ)
                        memcpy(bp->b_data, base, bp->b_bcount);
                else
                        memcpy(base, bp->b_data, bp->b_bcount);
                bp->b_resid = 0;
                biodone(bp);
        } else if (mfsp->mfs_proc == curproc) {
                mfs_doio(bp, mfsp->mfs_baseoff);
        } else if (doing_shutdown) {
                /*
                 * bitbucket I/O during shutdown.
                 * Note that reads should *not* happen here, but..
                 */
                if (bp->b_flags & B_READ)
                        printf("warning: mfs read during shutdown\n");
                bp->b_resid = 0;
                biodone(bp);
        } else {
                mutex_enter(&mfs_lock);
                bufq_put(mfsp->mfs_buflist, bp);
                cv_broadcast(&mfsp->mfs_cv);
                mutex_exit(&mfs_lock);
        }
        return (0);
}

/*
 * Memory file system I/O.
 */
void
mfs_doio(struct buf *bp, void *base)
{

        base = (char *)base + (bp->b_blkno << DEV_BSHIFT);
        if (bp->b_flags & B_READ)
                bp->b_error = copyin(base, bp->b_data, bp->b_bcount);
        else
                bp->b_error = copyout(bp->b_data, base, bp->b_bcount);
        if (bp->b_error == 0)
                bp->b_resid = 0;
        biodone(bp);
}

/*
 * This is a noop, simply returning what one has been given.
 */
int
mfs_bmap(void *v)
{
        struct vop_bmap_args /* {
                struct vnode *a_vp;
                daddr_t  a_bn;
                struct vnode **a_vpp;
                daddr_t *a_bnp;
                int *a_runp;
        } */ *ap = v;

        if (ap->a_vpp != NULL)
                *ap->a_vpp = ap->a_vp;
        if (ap->a_bnp != NULL)
                *ap->a_bnp = ap->a_bn;
        if (ap->a_runp != NULL)
                 *ap->a_runp = 0;
        return (0);
}

/*
 * Memory filesystem close routine
 */
/* ARGSUSED */
int
mfs_close(void *v)
{
        struct vop_close_args /* {
                struct vnode *a_vp;
                int  a_fflag;
                kauth_cred_t a_cred;
        } */ *ap = v;
        struct vnode *vp = ap->a_vp;
        struct mfsnode *mfsp = VTOMFS(vp);
        struct buf *bp;
        int error;

        /*
         * Finish any pending I/O requests.
         */
        mutex_enter(&mfs_lock);
        while ((bp = bufq_get(mfsp->mfs_buflist)) != NULL) {
                mutex_exit(&mfs_lock);
                mfs_doio(bp, mfsp->mfs_baseoff);
                mutex_enter(&mfs_lock);
        }
        mutex_exit(&mfs_lock);
        /*
         * On last close of a memory filesystem
         * we must invalidate any in core blocks, so that
         * we can, free up its vnode.
         */
        if ((error = vinvalbuf(vp, V_SAVE, ap->a_cred, curlwp, 0, 0)) != 0)
                return (error);
        /*
         * There should be no way to have any more uses of this
         * vnode, so if we find any other uses, it is a panic.
         */
        if (bufq_peek(mfsp->mfs_buflist) != NULL)
                panic("mfs_close");
        /*
         * Send a request to the filesystem server to exit.
         */
        mutex_enter(&mfs_lock);
        mfsp->mfs_shutdown = 1;
        cv_broadcast(&mfsp->mfs_cv);
        mutex_exit(&mfs_lock);
        return (0);
}

/*
 * Memory filesystem inactive routine
 */
/* ARGSUSED */
int
mfs_inactive(void *v)
{
        struct vop_inactive_v2_args /* {
                struct vnode *a_vp;
        } */ *ap = v;
        struct vnode *vp = ap->a_vp;
        struct mfsnode *mfsp = VTOMFS(vp);

        if (bufq_peek(mfsp->mfs_buflist) != NULL)
                panic("mfs_inactive: not inactive (mfs_buflist %p)",
                        bufq_peek(mfsp->mfs_buflist));

        return VOCALL(spec_vnodeop_p,  VOFFSET(vop_inactive), ap);
}

/*
 * Reclaim a memory filesystem devvp so that it can be reused.
 */
int
mfs_reclaim(void *v)
{
        struct vop_reclaim_v2_args /* {
                struct vnode *a_vp;
        } */ *ap = v;
        struct vnode *vp = ap->a_vp;
        struct mfsnode *mfsp = VTOMFS(vp);
        int refcnt;

        mutex_enter(&mfs_lock);
        vp->v_data = NULL;
        refcnt = --mfsp->mfs_refcnt;
        mutex_exit(&mfs_lock);

        if (refcnt == 0) {
                bufq_free(mfsp->mfs_buflist);
                cv_destroy(&mfsp->mfs_cv);
                kmem_free(mfsp, sizeof(*mfsp));
        }

        return VOCALL(spec_vnodeop_p,  VOFFSET(vop_reclaim), ap);
}

/*
 * Print out the contents of an mfsnode.
 */
int
mfs_print(void *v)
{
        struct vop_print_args /* {
                struct vnode *a_vp;
        } */ *ap = v;
        struct mfsnode *mfsp = VTOMFS(ap->a_vp);

        printf("tag VT_MFS, pid %d, base %p, size %ld\n",
            (mfsp->mfs_proc != NULL) ? mfsp->mfs_proc->p_pid : 0,
            mfsp->mfs_baseoff, mfsp->mfs_size);
        return (0);
}