/*
* Copyright (c) 2005, 2006 Antti Kantee. All Rights Reserved.
*
* Development of this software was supported by the
* Google Summer of Code program and the Ulla Tuominen Foundation.
* The Google SoC project was mentored by Bill Studenmund.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
*/
/*
* Try to ensure data structures used by the puffs protocol
* do not unexpectedly change.
*/
#if defined(__i386__) && defined(__ELF__)
CTASSERT(sizeof(struct puffs_kargs) == 3928);
CTASSERT(sizeof(struct vattr) == 136);
CTASSERT(sizeof(struct puffs_req) == 44);
#endif
/* build real name */
(void)strlcpy(fstype, PUFFS_TYPEPREFIX, sizeof(fstype));
(void)strlcat(fstype, args->pa_typename, sizeof(fstype));
/* inform user server if it got the max request size it wanted */
if (args->pa_maxmsglen == 0 || args->pa_maxmsglen > PUFFS_MSG_MAXSIZE)
args->pa_maxmsglen = PUFFS_MSG_MAXSIZE;
else if (args->pa_maxmsglen < 2*PUFFS_MSGSTRUCT_MAX)
args->pa_maxmsglen = 2*PUFFS_MSGSTRUCT_MAX;
/*
* We can't handle the VFS_STATVFS() mount_domount() does
* after VFS_MOUNT() because we'd deadlock, so handle it
* here already.
*/
struct statvfs *sb = STATVFSBUF_GET();
puffs_statvfs_to_statvfs(&args->pa_svfsb, sb);
copy_statvfs_info(sb, mp);
STATVFSBUF_PUT(sb);
mp->mnt_fs_bshift = DEV_BSHIFT;
mp->mnt_dev_bshift = DEV_BSHIFT;
mp->mnt_flag &= ~MNT_LOCAL; /* we don't really know, so ... */
mp->mnt_data = pmp;
#if 0
/*
* XXX: puffs code is MPSAFE. However, VFS really isn't.
* Currently, there is nothing which protects an inode from
* reclaim while there are threads inside the file system.
* This means that in the event of a server crash, an MPSAFE
* mount is likely to end up accessing invalid memory. For the
* non-mpsafe case, the kernel lock, general structure of
* puffs and pmp_refcount protect the threads during escape.
*
* Fixing this will require:
* a) fixing vfs
* OR
* b) adding a small sleep to puffs_msgif_close() between
* userdead() and dounmount().
* (well, this isn't really a fix, but would solve
* 99.999% of the race conditions).
*
* Also, in the event of "b", unmount -f should be used,
* like with any other file system, sparingly and only when
* it is "known" to be safe.
*/
mp->mnt_iflags |= IMNT_MPSAFE;
#endif
/*
* Inform the fileops processing code that we have a mountpoint.
* If it doesn't know about anyone with our pid/fd having the
* device open, punt
*/
if ((pmp->pmp_pi
= putter_attach(mntpid, args->pa_fd, pmp, &puffs_putter)) == NULL) {
error = ENOENT;
goto out;
}
/* XXX: check parameters */
pmp->pmp_root_cookie = args->pa_root_cookie;
switch (args->pa_root_vtype) {
case VNON: case VREG: case VDIR: case VBLK:
case VCHR: case VLNK: case VSOCK: case VFIFO:
break;
default:
error = EINVAL;
goto out;
}
pmp->pmp_root_vtype = args->pa_root_vtype;
DPRINTF(("puffs_unmount: detach filesystem from vfs, current "
"status 0x%x\n", pmp->pmp_status));
/*
* flush all the vnodes. VOP_RECLAIM() takes care that the
* root vnode does not get flushed until unmount. The
* userspace root node cookie is stored in the mount
* structure, so we can always re-instantiate a root vnode,
* should userspace unmount decide it doesn't want to
* cooperate.
*/
error = vflush(mp, NULLVP, force ? FORCECLOSE : 0);
if (error)
goto out;
/*
* If we are not DYING, we should ask userspace's opinion
* about the situation
*/
mutex_enter(&pmp->pmp_lock);
if (pmp->pmp_status != PUFFSTAT_DYING) {
pmp->pmp_unmounting = 1;
mutex_exit(&pmp->pmp_lock);
/*
* if userspace cooperated or we really need to die,
* screw what userland thinks and just die.
*/
if (error == 0 || force) {
struct puffs_sopreq *psopr;
/* tell waiters & other resources to go unwait themselves */
puffs_userdead(pmp);
putter_detach(pmp->pmp_pi);
/*
* Wait until there are no more users for the mount resource.
* Notice that this is hooked against transport_close
* and return from touser. In an ideal world, it would
* be hooked against final return from all operations.
* But currently it works well enough, since nobody
* does weird blocking voodoo after return from touser().
*/
while (pmp->pmp_refcount != 0)
cv_wait(&pmp->pmp_refcount_cv, &pmp->pmp_lock);
mutex_exit(&pmp->pmp_lock);
/*
* Release kernel thread now that there is nothing
* it would be wanting to lock.
*/
KASSERT(curlwp != uvm.pagedaemon_lwp);
psopr = kmem_alloc(sizeof(*psopr), KM_SLEEP);
psopr->psopr_sopreq = PUFFS_SOPREQSYS_EXIT;
mutex_enter(&pmp->pmp_sopmtx);
if (pmp->pmp_sopthrcount == 0) {
mutex_exit(&pmp->pmp_sopmtx);
kmem_free(psopr, sizeof(*psopr));
mutex_enter(&pmp->pmp_sopmtx);
KASSERT(pmp->pmp_sopthrcount == 0);
} else {
TAILQ_INSERT_TAIL(&pmp->pmp_sopfastreqs,
psopr, psopr_entries);
cv_signal(&pmp->pmp_sopcv);
}
while (pmp->pmp_sopthrcount > 0)
cv_wait(&pmp->pmp_sopcv, &pmp->pmp_sopmtx);
mutex_exit(&pmp->pmp_sopmtx);
/* free resources now that we hopefully have no waiters left */
cv_destroy(&pmp->pmp_unmounting_cv);
cv_destroy(&pmp->pmp_refcount_cv);
cv_destroy(&pmp->pmp_msg_waiter_cv);
cv_destroy(&pmp->pmp_sopcv);
mutex_destroy(&pmp->pmp_lock);
mutex_destroy(&pmp->pmp_sopmtx);
/*
* This doesn't need to travel to userspace
*/
int
puffs_vfsop_root(struct mount *mp, int lktype, struct vnode **vpp)
{
struct puffs_mount *pmp = MPTOPUFFSMP(mp);
int rv;
int
puffs_vfsop_statvfs(struct mount *mp, struct statvfs *sbp)
{
PUFFS_MSG_VARS(vfs, statvfs);
struct puffs_mount *pmp;
int error = 0;
pmp = MPTOPUFFSMP(mp);
/*
* If we are mounting, it means that the userspace counterpart
* is calling mount(2), but mount(2) also calls statvfs. So
* requesting statvfs from userspace would mean a deadlock.
* Compensate.
*/
if (__predict_false(pmp->pmp_status == PUFFSTAT_MOUNTING))
return EINPROGRESS;
/*
* Try to produce a sensible result even in the event
* of userspace error.
*
* XXX: cache the copy in non-error case
*/
if (!error) {
puffs_statvfs_to_statvfs(&statvfs_msg->pvfsr_sb, sbp);
}
copy_statvfs_info(sbp, mp);
if (!error) {
statvfs_to_puffs_statvfs(sbp, &statvfs_msg->pvfsr_sb);
}
/*
* Sync all cached data from regular vnodes (which are not
* currently locked, see below). After this we call VFS_SYNC
* for the fs server, which should handle data and metadata for
* all the nodes it knows to exist.
*/
vfs_vnode_iterator_init(mp, &marker);
while ((vp = vfs_vnode_iterator_next(marker, pageflush_selector,
NULL)))
{
/*
* Here we try to get a reference to the vnode and to
* lock it. This is mostly cargo-culted, but I will
* offer an explanation to why I believe this might
* actually do the right thing.
*
* If the vnode is a goner, we quite obviously don't need
* to sync it.
*
* If the vnode was busy, we don't need to sync it because
* this is never called with MNT_WAIT except from
* dounmount(), when we are wait-flushing all the dirty
* vnodes through other routes in any case. So there,
* sync() doesn't actually sync. Happy now?
*/
error = vn_lock(vp, LK_EXCLUSIVE | LK_NOWAIT);
if (error) {
vrele(vp);
continue;
}
pn = VPTOPP(vp);
/* hmm.. is the FAF thing entirely sensible? */
if (waitfor == MNT_LAZY) {
mutex_enter(vp->v_interlock);
pn->pn_stat |= PNODE_FAF;
mutex_exit(vp->v_interlock);
}
rv = VOP_FSYNC(vp, cred, fsyncwait, 0, 0);
if (waitfor == MNT_LAZY) {
mutex_enter(vp->v_interlock);
pn->pn_stat &= ~PNODE_FAF;
mutex_exit(vp->v_interlock);
}
if (rv)
error = rv;
vput(vp);
}
vfs_vnode_iterator_destroy(marker);
return error;
}
int
puffs_vfsop_sync(struct mount *mp, int waitfor, struct kauth_cred *cred)
{
PUFFS_MSG_VARS(vfs, sync);
struct puffs_mount *pmp = MPTOPUFFSMP(mp);
int error, rv;