Index: head/share/man/man9/VOP_LOCK.9 =================================================================== --- head/share/man/man9/VOP_LOCK.9 (revision 174950) +++ head/share/man/man9/VOP_LOCK.9 (revision 174951) @@ -1,187 +1,185 @@ .\" -*- nroff -*- .\" .\" Copyright (c) 1996 Doug Rabson .\" .\" All rights reserved. .\" .\" This program is free software. .\" .\" 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 DEVELOPERS ``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 DEVELOPERS 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. .\" .\" $FreeBSD$ .\" .Dd December 27, 2007 .Os .Dt VOP_LOCK 9 .Sh NAME .Nm VOP_LOCK , .Nm VOP_UNLOCK , .Nm VOP_ISLOCKED , .Nm vn_lock .Nd serialize access to a vnode .Sh SYNOPSIS .In sys/param.h .In sys/lock.h .In sys/vnode.h .Ft int .Fn VOP_LOCK "struct vnode *vp" "int flags" "struct thread *td" .Ft int .Fn VOP_UNLOCK "struct vnode *vp" "int flags" "struct thread *td" .Ft int .Fn VOP_ISLOCKED "struct vnode *vp" "struct thread *td" .Ft int .Fn vn_lock "struct vnode *vp" "int flags" "struct thread *td" .Sh DESCRIPTION These calls are used to serialize access to the file system, such as to prevent two writes to the same file from happening at the same time. .Pp The arguments are: .Bl -tag -width flags .It Fa vp The vnode being locked or unlocked. .It Fa flags One of the lock request types: .Pp -.Bl -tag -width ".Dv LK_EXCLUPGRADE" -offset indent -compact +.Bl -tag -width ".Dv LK_CANRECURSE" -offset indent -compact .It Dv LK_SHARED Shared lock. .It Dv LK_EXCLUSIVE Exclusive lock. .It Dv LK_UPGRADE Shared-to-exclusive upgrade. -.It Dv LK_EXCLUPGRADE -First shared-to-exclusive upgrade. .It Dv LK_DOWNGRADE Exclusive-to-shared downgrade. .It Dv LK_RELEASE Release any type of lock. .It Dv LK_DRAIN Wait for all lock activity to end. .El .Pp The lock type may be .Em or Ns 'ed with these lock flags: .Pp -.Bl -tag -width ".Dv LK_EXCLUPGRADE" -offset indent -compact +.Bl -tag -width ".Dv LK_CANRECURSE" -offset indent -compact .It Dv LK_NOWAIT Do not sleep to wait for lock. .It Dv LK_SLEEPFAIL Sleep, then return failure. .It Dv LK_CANRECURSE Allow recursive exclusive lock. .El .Pp The lock type may be .Em or Ns 'ed with these control flags: .Pp -.Bl -tag -width ".Dv LK_EXCLUPGRADE" -offset indent -compact +.Bl -tag -width ".Dv LK_CANRECURSE" -offset indent -compact .It Dv LK_INTERLOCK Specify when the caller already has a simple lock .Fn ( VOP_LOCK will unlock the simple lock after getting the lock). .It Dv LK_RETRY Retry until locked. .El .It Fa td Thread context to use for the locks. .El .Pp Kernel code should use .Fn vn_lock to lock a vnode rather than calling .Fn VOP_LOCK directly. .Sh RETURN VALUES Zero is returned on success, otherwise an error is returned. .Sh PSEUDOCODE .Bd -literal struct vopnode { int von_flag; /* * Other file system specific data. */ ...; }; #define VON_LOCKED 1 #define VON_WANTED 2 #define VTOVON(vp) ((struct vopnode *) (vp)->v_data) int vop_lock(struct vnode *vp) { struct vopnode* vop; start: while (vp->v_flag & VXLOCK) { vp->v_flag |= VXWANT; tsleep((caddr_t)vp, PINOD, "voplk1", 0); } if (vp->v_tag == VT_NON) return ENOENT; vop = VTOVON(vp); if (vop->von_flag & VON_LOCKED) { vop->von_flag |= VON_WANTED; tsleep((caddr_t) vop, PINOD, "voplk2", 0); goto start; } vop->von_flag |= VON_LOCKED; return 0; } int vop_unlock(struct vnode *vp) { struct vopnode *vop = VTOVON(vp); if ((vop->von_flag & VON_LOCKED) == 0) { panic("vop_unlock not locked"); } vop->von_flag &= ~VON_LOCKED; if (vop->von_flag & VON_WANTED) { vop->von_flag &= ~VON_WANTED; wakeup((caddr_t) vop); } return 0; } int vop_islocked(struct vnode *vp) { struct vopnode *vop = VTOVON(vp); if (vop->von_flag & VON_LOCKED) return 1; else return 0; } .Ed .Sh SEE ALSO .Xr vnode 9 .Sh AUTHORS This manual page was written by .An Doug Rabson . Index: head/share/man/man9/lock.9 =================================================================== --- head/share/man/man9/lock.9 (revision 174950) +++ head/share/man/man9/lock.9 (revision 174951) @@ -1,286 +1,278 @@ .\" .\" Copyright (C) 2002 Chad David . 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(s), this list of conditions and the following disclaimer as .\" the first lines of this file unmodified other than the possible .\" addition of one or more copyright notices. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice(s), 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 COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. .\" .\" $FreeBSD$ .\" -.Dd June 20, 2006 +.Dd December 27, 2007 .Dt LOCK 9 .Os .Sh NAME .Nm lockinit , .Nm lockdestroy , .Nm lockcount , .Nm lockmgr , .Nm lockstatus , .Nm lockmgr_printinfo .Nd "lockmgr family of functions" .Sh SYNOPSIS .In sys/types.h .In sys/lockmgr.h .Ft void .Fn lockinit "struct lock *lkp" "int prio" "const char *wmesg" "int timo" "int flags" .Ft void .Fn lockdestroy "struct lock *lkp" .Ft int .Fn lockcount "struct lock *lkp" .Ft int .Fn lockmgr "struct lock *lkp" "u_int flags" "struct mtx *interlkp" "struct thread *td" .Ft int .Fn lockstatus "struct lock *lkp" "struct thread *td" .Ft void .Fn lockmgr_printinfo "struct lock *lkp" .Sh DESCRIPTION The .Fn lockinit function is used to initialize a lock. It must be called before any operation can be performed on a lock. Its arguments are: .Bl -tag -width ".Fa wmesg" .It Fa lkp A pointer to the lock to initialize. .It Fa prio The priority passed to .Xr sleep 9 . .It Fa wmesg The lock message. This is used for both debugging output and .Xr sleep 9 . .It Fa timo The timeout value passed to .Xr sleep 9 . .It Fa flags The flags the lock is to be initialized with. .Bl -tag -width ".Dv LG_CANRECURSE" .It Dv LK_NOWAIT Do not sleep while acquiring the lock. .It Dv LK_SLEEPFAIL Fail after a sleep. .It Dv LK_CANRECURSE Allow recursive exclusive locks. .It Dv LK_NOSHARE Allow exclusive locks only. .It Dv LK_TIMELOCK Use .Fa timo during a sleep; otherwise, 0 is used. .El .El .Pp The .Fn lockdestroy function is used to destroy a lock, and while it is called in a number of places in the kernel, it currently does nothing. .Pp The .Fn lockcount function returns a count of the number of exclusive locks and shared locks held against the lock .Fa lkp . .Pp The .Fn lockmgr function handles general locking functionality within the kernel, including support for shared and exclusive locks, and recursion. .Fn lockmgr is also able to upgrade and downgrade locks. .Pp Its arguments are: .Bl -tag -width ".Fa interlkp" .It Fa lkp A pointer to the lock to manipulate. .It Fa flags Flags indicating what action is to be taken. -.Bl -tag -width ".Dv LK_EXCLUPGRADE" +.Bl -tag -width ".Dv LK_CANRECURSE" .It Dv LK_SHARED Acquire a shared lock. If an exclusive lock is currently held, it will be downgraded. .It Dv LK_EXCLUSIVE Acquire an exclusive lock. If an exclusive lock is already held, and .Dv LK_CANRECURSE is not set, the system will .Xr panic 9 . .It Dv LK_DOWNGRADE Downgrade exclusive lock to a shared lock. Downgrading a shared lock is not permitted. If an exclusive lock has been recursed, all references will be downgraded. -.It Dv LK_EXCLUPGRADE -Upgrade a shared lock to an exclusive lock. -Fails with -.Er EBUSY -if there is someone ahead of you in line waiting for an upgrade. -If this call fails, the shared lock is lost. -Attempts to upgrade an exclusive lock will cause a -.Xr panic 9 . .It Dv LK_UPGRADE Upgrade a shared lock to an exclusive lock. If this call fails, the shared lock is lost. During the upgrade, the shared lock could be temporarily dropped. Attempts to upgrade an exclusive lock will cause a .Xr panic 9 . .It Dv LK_RELEASE Release the lock. Releasing a lock that is not held can cause a .Xr panic 9 . .It Dv LK_DRAIN Wait for all activity on the lock to end, then mark it decommissioned. This is used before freeing a lock that is part of a piece of memory that is about to be freed. (As documented in .In sys/lockmgr.h . ) .It Dv LK_SLEEPFAIL Fail if operation has slept. .It Dv LK_NOWAIT Do not allow the call to sleep. This can be used to test the lock. .It Dv LK_CANRECURSE Allow recursion on an exclusive lock. For every lock there must be a release. .It Dv LK_INTERLOCK Unlock the interlock (which should be locked already). .El .It Fa interlkp An interlock mutex for controlling group access to the lock. If .Dv LK_INTERLOCK is specified, .Fn lockmgr assumes .Fa interlkp is currently owned and not recursed, and will return it unlocked. See .Xr mtx_assert 9 . .It Fa td A thread responsible for this call. .Dv NULL becomes .Dv LK_KERNPROC . .El .Pp The .Fn lockstatus function returns the status of the lock in relation to the .Vt thread passed to it. Note that if .Fa td is .Dv NULL and an exclusive lock is held, .Dv LK_EXCLUSIVE will be returned. .Pp The .Fn lockmgr_printinfo function prints debugging information about the lock. It is used primarily by .Xr VOP_PRINT 9 functions. .Sh RETURN VALUES The .Fn lockcount function returns an integer greater than or equal to zero. .Pp The .Fn lockmgr function returns 0 on success and non-zero on failure. .Pp The .Fn lockstatus function returns: .Bl -tag -width ".Dv LK_EXCLUSIVE" .It Dv LK_EXCLUSIVE An exclusive lock is held by the thread .Fa td . .It Dv LK_EXCLOTHER An exclusive lock is held by someone other than the thread .Fa td . .It Dv LK_SHARED A shared lock is held. .It Li 0 The lock is not held by anyone. .El .Sh ERRORS .Fn lockmgr fails if: .Bl -tag -width Er .It Bq Er EBUSY .Dv LK_FORCEUPGRADE was requested and another thread had already requested a lock upgrade. .It Bq Er EBUSY .Dv LK_NOWAIT was set, and a sleep would have been required. .It Bq Er ENOLCK .Dv LK_SLEEPFAIL was set and .Fn lockmgr did sleep. .It Bq Er EINTR .Dv PCATCH was set in the lock priority, and a signal was delivered during a sleep. Note the .Er ERESTART error below. .It Bq Er ERESTART .Dv PCATCH was set in the lock priority, a signal was delivered during a sleep, and the system call is to be restarted. .It Bq Er EWOULDBLOCK a non-zero timeout was given, and the timeout expired. .El .Sh LOCKS If .Dv LK_INTERLOCK is passed in the .Fa flags argument to .Fn lockmgr , the .Fa interlkp must be held prior to calling .Fn lockmgr , and will be returned unlocked. .Pp Upgrade attempts that fail result in the loss of the lock that is currently held. Also, it is invalid to upgrade an exclusive lock, and a .Xr panic 9 will be the result of trying. .Sh SEE ALSO .Xr condvar 9 , .Xr locking 9 , .Xr mutex 9 , .Xr rwlock 9 , .Xr sleep 9 , .Xr sx 9 , .Xr mtx_assert 9 , .Xr panic 9 , .Xr VOP_PRINT 9 .Sh AUTHORS This manual page was written by .An Chad David Aq davidc@acns.ab.ca . Index: head/sys/fs/unionfs/union_vnops.c =================================================================== --- head/sys/fs/unionfs/union_vnops.c (revision 174950) +++ head/sys/fs/unionfs/union_vnops.c (revision 174951) @@ -1,2331 +1,2330 @@ /*- * Copyright (c) 1992, 1993, 1994, 1995 Jan-Simon Pendry. * Copyright (c) 1992, 1993, 1994, 1995 * The Regents of the University of California. * Copyright (c) 2005, 2006 Masanori Ozawa , ONGS Inc. * Copyright (c) 2006 Daichi Goto * All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * 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. * 4. 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. * * @(#)union_vnops.c 8.32 (Berkeley) 6/23/95 * $FreeBSD$ * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if 0 #define UNIONFS_INTERNAL_DEBUG(msg, args...) printf(msg, ## args) #define UNIONFS_IDBG_RENAME #else #define UNIONFS_INTERNAL_DEBUG(msg, args...) #endif /* lockmgr lock <-> reverse table */ struct lk_lr_table { int lock; int revlock; }; static struct lk_lr_table un_llt[] = { {LK_SHARED, LK_RELEASE}, {LK_EXCLUSIVE, LK_RELEASE}, {LK_UPGRADE, LK_DOWNGRADE}, - {LK_EXCLUPGRADE, LK_DOWNGRADE}, {LK_DOWNGRADE, LK_UPGRADE}, {0, 0} }; static int unionfs_lookup(struct vop_cachedlookup_args *ap) { int iswhiteout; int lockflag; int error , uerror, lerror; u_long nameiop; u_long cnflags, cnflagsbk; struct unionfs_node *dunp; struct vnode *dvp, *udvp, *ldvp, *vp, *uvp, *lvp, *dtmpvp; struct vattr va; struct componentname *cnp; struct thread *td; iswhiteout = 0; lockflag = 0; error = uerror = lerror = ENOENT; cnp = ap->a_cnp; nameiop = cnp->cn_nameiop; cnflags = cnp->cn_flags; dvp = ap->a_dvp; dunp = VTOUNIONFS(dvp); udvp = dunp->un_uppervp; ldvp = dunp->un_lowervp; vp = uvp = lvp = NULLVP; td = curthread; *(ap->a_vpp) = NULLVP; UNIONFS_INTERNAL_DEBUG("unionfs_lookup: enter: nameiop=%ld, flags=%lx, path=%s\n", nameiop, cnflags, cnp->cn_nameptr); if (dvp->v_type != VDIR) return (ENOTDIR); /* * If read-only and op is not LOOKUP, will return EROFS. */ if ((cnflags & ISLASTCN) && (dvp->v_mount->mnt_flag & MNT_RDONLY) && LOOKUP != nameiop) return (EROFS); /* * lookup dotdot */ if (cnflags & ISDOTDOT) { if (LOOKUP != nameiop && udvp == NULLVP) return (EROFS); if (udvp != NULLVP) { dtmpvp = udvp; if (ldvp != NULLVP) VOP_UNLOCK(ldvp, 0, td); } else dtmpvp = ldvp; error = VOP_LOOKUP(dtmpvp, &vp, cnp); if (dtmpvp == udvp && ldvp != NULLVP) { VOP_UNLOCK(udvp, 0, td); vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td); } if (error == 0) { /* * Exchange lock and reference from vp to * dunp->un_dvp. vp is upper/lower vnode, but it * will need to return the unionfs vnode. */ if (nameiop == DELETE || nameiop == RENAME || (cnp->cn_lkflags & LK_TYPE_MASK)) VOP_UNLOCK(vp, 0, td); vrele(vp); VOP_UNLOCK(dvp, 0, td); *(ap->a_vpp) = dunp->un_dvp; vref(dunp->un_dvp); if (nameiop == DELETE || nameiop == RENAME) vn_lock(dunp->un_dvp, LK_EXCLUSIVE | LK_RETRY, td); else if (cnp->cn_lkflags & LK_TYPE_MASK) vn_lock(dunp->un_dvp, cnp->cn_lkflags | LK_RETRY, td); vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td); } else if (error == ENOENT && (cnflags & MAKEENTRY) && nameiop != CREATE) cache_enter(dvp, NULLVP, cnp); UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", error); return (error); } /* * lookup upper layer */ if (udvp != NULLVP) { uerror = VOP_LOOKUP(udvp, &uvp, cnp); if (uerror == 0) { if (udvp == uvp) { /* is dot */ vrele(uvp); *(ap->a_vpp) = dvp; vref(dvp); UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", uerror); return (uerror); } if (nameiop == DELETE || nameiop == RENAME || (cnp->cn_lkflags & LK_TYPE_MASK)) VOP_UNLOCK(uvp, 0, td); } /* check whiteout */ if (uerror == ENOENT || uerror == EJUSTRETURN) if (cnp->cn_flags & ISWHITEOUT) iswhiteout = 1; /* don't lookup lower */ if (iswhiteout == 0 && ldvp != NULLVP) if (VOP_GETATTR(udvp, &va, cnp->cn_cred, td) == 0 && (va.va_flags & OPAQUE)) iswhiteout = 1; /* don't lookup lower */ #if 0 UNIONFS_INTERNAL_DEBUG("unionfs_lookup: debug: whiteout=%d, path=%s\n", iswhiteout, cnp->cn_nameptr); #endif } /* * lookup lower layer */ if (ldvp != NULLVP && !(cnflags & DOWHITEOUT) && iswhiteout == 0) { /* always op is LOOKUP */ cnp->cn_nameiop = LOOKUP; cnflagsbk = cnp->cn_flags; cnp->cn_flags = cnflags; lerror = VOP_LOOKUP(ldvp, &lvp, cnp); cnp->cn_nameiop = nameiop; if (udvp != NULLVP && (uerror == 0 || uerror == EJUSTRETURN)) cnp->cn_flags = cnflagsbk; if (lerror == 0) { if (ldvp == lvp) { /* is dot */ if (uvp != NULLVP) vrele(uvp); /* no need? */ vrele(lvp); *(ap->a_vpp) = dvp; vref(dvp); UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", lerror); return (lerror); } if (cnp->cn_lkflags & LK_TYPE_MASK) VOP_UNLOCK(lvp, 0, td); } } /* * check lookup result */ if (uvp == NULLVP && lvp == NULLVP) { UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", (udvp != NULLVP ? uerror : lerror)); return (udvp != NULLVP ? uerror : lerror); } /* * check vnode type */ if (uvp != NULLVP && lvp != NULLVP && uvp->v_type != lvp->v_type) { vrele(lvp); lvp = NULLVP; } /* * check shadow dir */ if (uerror != 0 && uerror != EJUSTRETURN && udvp != NULLVP && lerror == 0 && lvp != NULLVP && lvp->v_type == VDIR && !(dvp->v_mount->mnt_flag & MNT_RDONLY) && (1 < cnp->cn_namelen || '.' != *(cnp->cn_nameptr))) { /* get unionfs vnode in order to create a new shadow dir. */ error = unionfs_nodeget(dvp->v_mount, NULLVP, lvp, dvp, &vp, cnp, td); if (error != 0) goto unionfs_lookup_out; if (LK_SHARED == (cnp->cn_lkflags & LK_TYPE_MASK)) VOP_UNLOCK(vp, 0, td); if (LK_EXCLUSIVE != VOP_ISLOCKED(vp, td)) { vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); lockflag = 1; } error = unionfs_mkshadowdir(MOUNTTOUNIONFSMOUNT(dvp->v_mount), udvp, VTOUNIONFS(vp), cnp, td); if (lockflag != 0) VOP_UNLOCK(vp, 0, td); if (error != 0) { UNIONFSDEBUG("unionfs_lookup: Unable to create shadow dir."); if ((cnp->cn_lkflags & LK_TYPE_MASK) == LK_EXCLUSIVE) vput(vp); else vrele(vp); goto unionfs_lookup_out; } if ((cnp->cn_lkflags & LK_TYPE_MASK) == LK_SHARED) vn_lock(vp, LK_SHARED | LK_RETRY, td); } /* * get unionfs vnode. */ else { if (uvp != NULLVP) error = uerror; else error = lerror; if (error != 0) goto unionfs_lookup_out; error = unionfs_nodeget(dvp->v_mount, uvp, lvp, dvp, &vp, cnp, td); if (error != 0) { UNIONFSDEBUG("unionfs_lookup: Unable to create unionfs vnode."); goto unionfs_lookup_out; } if ((nameiop == DELETE || nameiop == RENAME) && (cnp->cn_lkflags & LK_TYPE_MASK) == 0) vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); } *(ap->a_vpp) = vp; if (cnflags & MAKEENTRY) cache_enter(dvp, vp, cnp); unionfs_lookup_out: if (uvp != NULLVP) vrele(uvp); if (lvp != NULLVP) vrele(lvp); if (error == ENOENT && (cnflags & MAKEENTRY) && nameiop != CREATE) cache_enter(dvp, NULLVP, cnp); UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", error); return (error); } static int unionfs_create(struct vop_create_args *ap) { struct unionfs_node *dunp; struct componentname *cnp; struct thread *td; struct vnode *udvp; struct vnode *vp; int error; UNIONFS_INTERNAL_DEBUG("unionfs_create: enter\n"); dunp = VTOUNIONFS(ap->a_dvp); cnp = ap->a_cnp; td = curthread; udvp = dunp->un_uppervp; error = EROFS; if (udvp != NULLVP) { if ((error = VOP_CREATE(udvp, &vp, cnp, ap->a_vap)) == 0) { VOP_UNLOCK(vp, 0, td); error = unionfs_nodeget(ap->a_dvp->v_mount, vp, NULLVP, ap->a_dvp, ap->a_vpp, cnp, td); vrele(vp); } } UNIONFS_INTERNAL_DEBUG("unionfs_create: leave (%d)\n", error); return (error); } static int unionfs_whiteout(struct vop_whiteout_args *ap) { struct unionfs_node *dunp; struct componentname *cnp; struct vnode *udvp; int error; UNIONFS_INTERNAL_DEBUG("unionfs_whiteout: enter\n"); dunp = VTOUNIONFS(ap->a_dvp); cnp = ap->a_cnp; udvp = dunp->un_uppervp; error = EOPNOTSUPP; if (udvp != NULLVP) { switch (ap->a_flags) { case CREATE: case DELETE: case LOOKUP: error = VOP_WHITEOUT(udvp, cnp, ap->a_flags); break; default: error = EINVAL; break; } } UNIONFS_INTERNAL_DEBUG("unionfs_whiteout: leave (%d)\n", error); return (error); } static int unionfs_mknod(struct vop_mknod_args *ap) { struct unionfs_node *dunp; struct componentname *cnp; struct thread *td; struct vnode *udvp; struct vnode *vp; int error; UNIONFS_INTERNAL_DEBUG("unionfs_mknod: enter\n"); dunp = VTOUNIONFS(ap->a_dvp); cnp = ap->a_cnp; td = curthread; udvp = dunp->un_uppervp; error = EROFS; if (udvp != NULLVP) { if ((error = VOP_MKNOD(udvp, &vp, cnp, ap->a_vap)) == 0) { VOP_UNLOCK(vp, 0, td); error = unionfs_nodeget(ap->a_dvp->v_mount, vp, NULLVP, ap->a_dvp, ap->a_vpp, cnp, td); vrele(vp); } } UNIONFS_INTERNAL_DEBUG("unionfs_mknod: leave (%d)\n", error); return (error); } static int unionfs_open(struct vop_open_args *ap) { int error; struct unionfs_node *unp; struct unionfs_node_status *unsp; struct vnode *uvp; struct vnode *lvp; struct vnode *targetvp; struct ucred *cred; struct thread *td; UNIONFS_INTERNAL_DEBUG("unionfs_open: enter\n"); error = 0; unp = VTOUNIONFS(ap->a_vp); uvp = unp->un_uppervp; lvp = unp->un_lowervp; targetvp = NULLVP; cred = ap->a_cred; td = ap->a_td; unionfs_get_node_status(unp, td, &unsp); if (unsp->uns_lower_opencnt > 0 || unsp->uns_upper_opencnt > 0) { /* vnode is already opend. */ if (unsp->uns_upper_opencnt > 0) targetvp = uvp; else targetvp = lvp; if (targetvp == lvp && (ap->a_mode & FWRITE) && lvp->v_type == VREG) targetvp = NULLVP; } if (targetvp == NULLVP) { if (uvp == NULLVP) { if ((ap->a_mode & FWRITE) && lvp->v_type == VREG) { error = unionfs_copyfile(unp, !(ap->a_mode & O_TRUNC), cred, td); if (error != 0) goto unionfs_open_abort; targetvp = uvp = unp->un_uppervp; } else targetvp = lvp; } else targetvp = uvp; } error = VOP_OPEN(targetvp, ap->a_mode, cred, td, ap->a_fp); if (error == 0) { if (targetvp == uvp) { if (uvp->v_type == VDIR && lvp != NULLVP && unsp->uns_lower_opencnt <= 0) { /* open lower for readdir */ error = VOP_OPEN(lvp, FREAD, cred, td, NULL); if (error != 0) { VOP_CLOSE(uvp, ap->a_mode, cred, td); goto unionfs_open_abort; } unsp->uns_node_flag |= UNS_OPENL_4_READDIR; unsp->uns_lower_opencnt++; } unsp->uns_upper_opencnt++; } else { unsp->uns_lower_opencnt++; unsp->uns_lower_openmode = ap->a_mode; } ap->a_vp->v_object = targetvp->v_object; } unionfs_open_abort: if (error != 0) unionfs_tryrem_node_status(unp, td, unsp); UNIONFS_INTERNAL_DEBUG("unionfs_open: leave (%d)\n", error); return (error); } static int unionfs_close(struct vop_close_args *ap) { int error; int locked; struct unionfs_node *unp; struct unionfs_node_status *unsp; struct ucred *cred; struct thread *td; struct vnode *ovp; UNIONFS_INTERNAL_DEBUG("unionfs_close: enter\n"); locked = 0; unp = VTOUNIONFS(ap->a_vp); cred = ap->a_cred; td = ap->a_td; if (VOP_ISLOCKED(ap->a_vp, td) != LK_EXCLUSIVE) { vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY, td); locked = 1; } unionfs_get_node_status(unp, td, &unsp); if (unsp->uns_lower_opencnt <= 0 && unsp->uns_upper_opencnt <= 0) { #ifdef DIAGNOSTIC printf("unionfs_close: warning: open count is 0\n"); #endif if (unp->un_uppervp != NULLVP) ovp = unp->un_uppervp; else ovp = unp->un_lowervp; } else if (unsp->uns_upper_opencnt > 0) ovp = unp->un_uppervp; else ovp = unp->un_lowervp; error = VOP_CLOSE(ovp, ap->a_fflag, cred, td); if (error != 0) goto unionfs_close_abort; ap->a_vp->v_object = ovp->v_object; if (ovp == unp->un_uppervp) { unsp->uns_upper_opencnt--; if (unsp->uns_upper_opencnt == 0) { if (unsp->uns_node_flag & UNS_OPENL_4_READDIR) { VOP_CLOSE(unp->un_lowervp, FREAD, cred, td); unsp->uns_node_flag &= ~UNS_OPENL_4_READDIR; unsp->uns_lower_opencnt--; } if (unsp->uns_lower_opencnt > 0) ap->a_vp->v_object = unp->un_lowervp->v_object; } } else unsp->uns_lower_opencnt--; unionfs_close_abort: unionfs_tryrem_node_status(unp, td, unsp); if (locked != 0) VOP_UNLOCK(ap->a_vp, 0, td); UNIONFS_INTERNAL_DEBUG("unionfs_close: leave (%d)\n", error); return (error); } /* * Check the access mode toward shadow file/dir. */ static int unionfs_check_corrected_access(u_short mode, struct vattr *va, struct ucred *cred) { int count; uid_t uid; /* upper side vnode's uid */ gid_t gid; /* upper side vnode's gid */ u_short vmode; /* upper side vnode's mode */ gid_t *gp; u_short mask; mask = 0; uid = va->va_uid; gid = va->va_gid; vmode = va->va_mode; /* check owner */ if (cred->cr_uid == uid) { if (mode & VEXEC) mask |= S_IXUSR; if (mode & VREAD) mask |= S_IRUSR; if (mode & VWRITE) mask |= S_IWUSR; return ((vmode & mask) == mask ? 0 : EACCES); } /* check group */ count = 0; gp = cred->cr_groups; for (; count < cred->cr_ngroups; count++, gp++) { if (gid == *gp) { if (mode & VEXEC) mask |= S_IXGRP; if (mode & VREAD) mask |= S_IRGRP; if (mode & VWRITE) mask |= S_IWGRP; return ((vmode & mask) == mask ? 0 : EACCES); } } /* check other */ if (mode & VEXEC) mask |= S_IXOTH; if (mode & VREAD) mask |= S_IROTH; if (mode & VWRITE) mask |= S_IWOTH; return ((vmode & mask) == mask ? 0 : EACCES); } static int unionfs_access(struct vop_access_args *ap) { struct unionfs_mount *ump; struct unionfs_node *unp; struct vnode *uvp; struct vnode *lvp; struct thread *td; struct vattr va; int mode; int error; UNIONFS_INTERNAL_DEBUG("unionfs_access: enter\n"); ump = MOUNTTOUNIONFSMOUNT(ap->a_vp->v_mount); unp = VTOUNIONFS(ap->a_vp); uvp = unp->un_uppervp; lvp = unp->un_lowervp; td = ap->a_td; mode = ap->a_mode; error = EACCES; if ((mode & VWRITE) && (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)) { switch (ap->a_vp->v_type) { case VREG: case VDIR: case VLNK: return (EROFS); default: break; } } if (uvp != NULLVP) { error = VOP_ACCESS(uvp, mode, ap->a_cred, td); UNIONFS_INTERNAL_DEBUG("unionfs_access: leave (%d)\n", error); return (error); } if (lvp != NULLVP) { if (mode & VWRITE) { if (ump->um_uppervp->v_mount->mnt_flag & MNT_RDONLY) { switch (ap->a_vp->v_type) { case VREG: case VDIR: case VLNK: return (EROFS); default: break; } } else if (ap->a_vp->v_type == VREG || ap->a_vp->v_type == VDIR) { /* check shadow file/dir */ if (ump->um_copymode != UNIONFS_TRANSPARENT) { error = unionfs_create_uppervattr(ump, lvp, &va, ap->a_cred, td); if (error != 0) return (error); error = unionfs_check_corrected_access( mode, &va, ap->a_cred); if (error != 0) return (error); } } mode &= ~VWRITE; mode |= VREAD; /* will copy to upper */ } error = VOP_ACCESS(lvp, mode, ap->a_cred, td); } UNIONFS_INTERNAL_DEBUG("unionfs_access: leave (%d)\n", error); return (error); } static int unionfs_getattr(struct vop_getattr_args *ap) { int error; struct unionfs_node *unp; struct unionfs_mount *ump; struct vnode *uvp; struct vnode *lvp; struct thread *td; struct vattr va; UNIONFS_INTERNAL_DEBUG("unionfs_getattr: enter\n"); unp = VTOUNIONFS(ap->a_vp); ump = MOUNTTOUNIONFSMOUNT(ap->a_vp->v_mount); uvp = unp->un_uppervp; lvp = unp->un_lowervp; td = ap->a_td; if (uvp != NULLVP) { if ((error = VOP_GETATTR(uvp, ap->a_vap, ap->a_cred, td)) == 0) ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0]; UNIONFS_INTERNAL_DEBUG("unionfs_getattr: leave mode=%o, uid=%d, gid=%d (%d)\n", ap->a_vap->va_mode, ap->a_vap->va_uid, ap->a_vap->va_gid, error); return (error); } error = VOP_GETATTR(lvp, ap->a_vap, ap->a_cred, td); if (error == 0 && !(ump->um_uppervp->v_mount->mnt_flag & MNT_RDONLY)) { /* correct the attr toward shadow file/dir. */ if (ap->a_vp->v_type == VREG || ap->a_vp->v_type == VDIR) { unionfs_create_uppervattr_core(ump, ap->a_vap, &va, td); ap->a_vap->va_mode = va.va_mode; ap->a_vap->va_uid = va.va_uid; ap->a_vap->va_gid = va.va_gid; } } if (error == 0) ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0]; UNIONFS_INTERNAL_DEBUG("unionfs_getattr: leave mode=%o, uid=%d, gid=%d (%d)\n", ap->a_vap->va_mode, ap->a_vap->va_uid, ap->a_vap->va_gid, error); return (error); } static int unionfs_setattr(struct vop_setattr_args *ap) { int error; struct unionfs_node *unp; struct vnode *uvp; struct vnode *lvp; struct thread *td; struct vattr *vap; UNIONFS_INTERNAL_DEBUG("unionfs_setattr: enter\n"); error = EROFS; unp = VTOUNIONFS(ap->a_vp); uvp = unp->un_uppervp; lvp = unp->un_lowervp; td = ap->a_td; vap = ap->a_vap; if ((ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) && (vap->va_flags != VNOVAL || vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL)) return (EROFS); if (uvp == NULLVP && lvp->v_type == VREG) { error = unionfs_copyfile(unp, (vap->va_size != 0), ap->a_cred, td); if (error != 0) return (error); uvp = unp->un_uppervp; } if (uvp != NULLVP) error = VOP_SETATTR(uvp, vap, ap->a_cred, td); UNIONFS_INTERNAL_DEBUG("unionfs_setattr: leave (%d)\n", error); return (error); } static int unionfs_read(struct vop_read_args *ap) { int error; struct unionfs_node *unp; struct vnode *tvp; /* UNIONFS_INTERNAL_DEBUG("unionfs_read: enter\n"); */ unp = VTOUNIONFS(ap->a_vp); tvp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); error = VOP_READ(tvp, ap->a_uio, ap->a_ioflag, ap->a_cred); /* UNIONFS_INTERNAL_DEBUG("unionfs_read: leave (%d)\n", error); */ return (error); } static int unionfs_write(struct vop_write_args *ap) { int error; struct unionfs_node *unp; struct vnode *tvp; /* UNIONFS_INTERNAL_DEBUG("unionfs_write: enter\n"); */ unp = VTOUNIONFS(ap->a_vp); tvp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); error = VOP_WRITE(tvp, ap->a_uio, ap->a_ioflag, ap->a_cred); /* UNIONFS_INTERNAL_DEBUG("unionfs_write: leave (%d)\n", error); */ return (error); } static int unionfs_lease(struct vop_lease_args *ap) { int error; struct unionfs_node *unp; struct vnode *vp; UNIONFS_INTERNAL_DEBUG("unionfs_lease: enter\n"); unp = VTOUNIONFS(ap->a_vp); vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); error = VOP_LEASE(vp, ap->a_td, ap->a_cred, ap->a_flag); UNIONFS_INTERNAL_DEBUG("unionfs_lease: lease (%d)\n", error); return (error); } static int unionfs_ioctl(struct vop_ioctl_args *ap) { int error; struct unionfs_node *unp; struct unionfs_node_status *unsp; struct vnode *ovp; UNIONFS_INTERNAL_DEBUG("unionfs_ioctl: enter\n"); vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY, ap->a_td); unp = VTOUNIONFS(ap->a_vp); unionfs_get_node_status(unp, ap->a_td, &unsp); ovp = (unsp->uns_upper_opencnt ? unp->un_uppervp : unp->un_lowervp); unionfs_tryrem_node_status(unp, ap->a_td, unsp); VOP_UNLOCK(ap->a_vp, 0, ap->a_td); if (ovp == NULLVP) return (EBADF); error = VOP_IOCTL(ovp, ap->a_command, ap->a_data, ap->a_fflag, ap->a_cred, ap->a_td); UNIONFS_INTERNAL_DEBUG("unionfs_ioctl: lease (%d)\n", error); return (error); } static int unionfs_poll(struct vop_poll_args *ap) { struct unionfs_node *unp; struct unionfs_node_status *unsp; struct vnode *ovp; vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY, ap->a_td); unp = VTOUNIONFS(ap->a_vp); unionfs_get_node_status(unp, ap->a_td, &unsp); ovp = (unsp->uns_upper_opencnt ? unp->un_uppervp : unp->un_lowervp); unionfs_tryrem_node_status(unp, ap->a_td, unsp); VOP_UNLOCK(ap->a_vp, 0, ap->a_td); if (ovp == NULLVP) return (EBADF); return (VOP_POLL(ovp, ap->a_events, ap->a_cred, ap->a_td)); } static int unionfs_fsync(struct vop_fsync_args *ap) { struct unionfs_node *unp; struct unionfs_node_status *unsp; struct vnode *ovp; unp = VTOUNIONFS(ap->a_vp); unionfs_get_node_status(unp, ap->a_td, &unsp); ovp = (unsp->uns_upper_opencnt ? unp->un_uppervp : unp->un_lowervp); unionfs_tryrem_node_status(unp, ap->a_td, unsp); if (ovp == NULLVP) return (EBADF); return (VOP_FSYNC(ovp, ap->a_waitfor, ap->a_td)); } static int unionfs_remove(struct vop_remove_args *ap) { int error; struct unionfs_node *dunp; struct unionfs_node *unp; struct unionfs_mount *ump; struct vnode *udvp; struct vnode *uvp; struct vnode *lvp; struct componentname *cnp; struct thread *td; UNIONFS_INTERNAL_DEBUG("unionfs_remove: enter\n"); error = 0; dunp = VTOUNIONFS(ap->a_dvp); unp = VTOUNIONFS(ap->a_vp); udvp = dunp->un_uppervp; uvp = unp->un_uppervp; lvp = unp->un_lowervp; cnp = ap->a_cnp; td = curthread; if (udvp == NULLVP) return (EROFS); if (uvp != NULLVP) { ump = MOUNTTOUNIONFSMOUNT(ap->a_vp->v_mount); if (ump->um_whitemode == UNIONFS_WHITE_ALWAYS || lvp != NULLVP) cnp->cn_flags |= DOWHITEOUT; error = VOP_REMOVE(udvp, uvp, cnp); } else if (lvp != NULLVP) error = unionfs_mkwhiteout(udvp, cnp, td, unp->un_path); UNIONFS_INTERNAL_DEBUG("unionfs_remove: leave (%d)\n", error); return (error); } static int unionfs_link(struct vop_link_args *ap) { int error; int needrelookup; struct unionfs_node *dunp; struct unionfs_node *unp; struct vnode *udvp; struct vnode *uvp; struct componentname *cnp; struct thread *td; UNIONFS_INTERNAL_DEBUG("unionfs_link: enter\n"); error = 0; needrelookup = 0; dunp = VTOUNIONFS(ap->a_tdvp); unp = NULL; udvp = dunp->un_uppervp; uvp = NULLVP; cnp = ap->a_cnp; td = curthread; if (udvp == NULLVP) return (EROFS); if (ap->a_vp->v_op != &unionfs_vnodeops) uvp = ap->a_vp; else { unp = VTOUNIONFS(ap->a_vp); if (unp->un_uppervp == NULLVP) { if (ap->a_vp->v_type != VREG) return (EOPNOTSUPP); error = unionfs_copyfile(unp, 1, cnp->cn_cred, td); if (error != 0) return (error); needrelookup = 1; } uvp = unp->un_uppervp; } if (needrelookup != 0) error = unionfs_relookup_for_create(ap->a_tdvp, cnp, td); if (error == 0) error = VOP_LINK(udvp, uvp, cnp); UNIONFS_INTERNAL_DEBUG("unionfs_link: leave (%d)\n", error); return (error); } static int unionfs_rename(struct vop_rename_args *ap) { int error; struct vnode *fdvp; struct vnode *fvp; struct componentname *fcnp; struct vnode *tdvp; struct vnode *tvp; struct componentname *tcnp; struct vnode *ltdvp; struct vnode *ltvp; struct thread *td; /* rename target vnodes */ struct vnode *rfdvp; struct vnode *rfvp; struct vnode *rtdvp; struct vnode *rtvp; int needrelookup; struct unionfs_mount *ump; struct unionfs_node *unp; UNIONFS_INTERNAL_DEBUG("unionfs_rename: enter\n"); error = 0; fdvp = ap->a_fdvp; fvp = ap->a_fvp; fcnp = ap->a_fcnp; tdvp = ap->a_tdvp; tvp = ap->a_tvp; tcnp = ap->a_tcnp; ltdvp = NULLVP; ltvp = NULLVP; td = curthread; rfdvp = fdvp; rfvp = fvp; rtdvp = tdvp; rtvp = tvp; needrelookup = 0; #ifdef DIAGNOSTIC if (!(fcnp->cn_flags & HASBUF) || !(tcnp->cn_flags & HASBUF)) panic("unionfs_rename: no name"); #endif /* check for cross device rename */ if (fvp->v_mount != tdvp->v_mount || (tvp != NULLVP && fvp->v_mount != tvp->v_mount)) { error = EXDEV; goto unionfs_rename_abort; } /* Renaming a file to itself has no effect. */ if (fvp == tvp) goto unionfs_rename_abort; /* * from/to vnode is unionfs node. */ unp = VTOUNIONFS(fdvp); #ifdef UNIONFS_IDBG_RENAME UNIONFS_INTERNAL_DEBUG("fdvp=%p, ufdvp=%p, lfdvp=%p\n", fdvp, unp->un_uppervp, unp->un_lowervp); #endif if (unp->un_uppervp == NULLVP) { error = ENODEV; goto unionfs_rename_abort; } rfdvp = unp->un_uppervp; vref(rfdvp); unp = VTOUNIONFS(fvp); #ifdef UNIONFS_IDBG_RENAME UNIONFS_INTERNAL_DEBUG("fvp=%p, ufvp=%p, lfvp=%p\n", fvp, unp->un_uppervp, unp->un_lowervp); #endif ump = MOUNTTOUNIONFSMOUNT(fvp->v_mount); if (unp->un_uppervp == NULLVP) { switch (fvp->v_type) { case VREG: if ((error = vn_lock(fvp, LK_EXCLUSIVE, td)) != 0) goto unionfs_rename_abort; error = unionfs_copyfile(unp, 1, fcnp->cn_cred, td); VOP_UNLOCK(fvp, 0, td); if (error != 0) goto unionfs_rename_abort; break; case VDIR: if ((error = vn_lock(fvp, LK_EXCLUSIVE, td)) != 0) goto unionfs_rename_abort; error = unionfs_mkshadowdir(ump, rfdvp, unp, fcnp, td); VOP_UNLOCK(fvp, 0, td); if (error != 0) goto unionfs_rename_abort; break; default: error = ENODEV; goto unionfs_rename_abort; } needrelookup = 1; } if (unp->un_lowervp != NULLVP) fcnp->cn_flags |= DOWHITEOUT; rfvp = unp->un_uppervp; vref(rfvp); unp = VTOUNIONFS(tdvp); #ifdef UNIONFS_IDBG_RENAME UNIONFS_INTERNAL_DEBUG("tdvp=%p, utdvp=%p, ltdvp=%p\n", tdvp, unp->un_uppervp, unp->un_lowervp); #endif if (unp->un_uppervp == NULLVP) { error = ENODEV; goto unionfs_rename_abort; } rtdvp = unp->un_uppervp; ltdvp = unp->un_lowervp; vref(rtdvp); if (tdvp == tvp) { rtvp = rtdvp; vref(rtvp); } else if (tvp != NULLVP) { unp = VTOUNIONFS(tvp); #ifdef UNIONFS_IDBG_RENAME UNIONFS_INTERNAL_DEBUG("tvp=%p, utvp=%p, ltvp=%p\n", tvp, unp->un_uppervp, unp->un_lowervp); #endif if (unp->un_uppervp == NULLVP) rtvp = NULLVP; else { if (tvp->v_type == VDIR) { error = EINVAL; goto unionfs_rename_abort; } rtvp = unp->un_uppervp; ltvp = unp->un_lowervp; vref(rtvp); } } if (needrelookup != 0) { if ((error = vn_lock(fdvp, LK_EXCLUSIVE, td)) != 0) goto unionfs_rename_abort; error = unionfs_relookup_for_delete(fdvp, fcnp, td); VOP_UNLOCK(fdvp, 0, td); if (error != 0) goto unionfs_rename_abort; /* Locke of tvp is canceled in order to avoid recursive lock. */ if (tvp != NULLVP && tvp != tdvp) VOP_UNLOCK(tvp, 0, td); error = unionfs_relookup_for_rename(tdvp, tcnp, td); if (tvp != NULLVP && tvp != tdvp) vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, td); if (error != 0) goto unionfs_rename_abort; } error = VOP_RENAME(rfdvp, rfvp, fcnp, rtdvp, rtvp, tcnp); if (error == 0) { if (rtvp != NULLVP && rtvp->v_type == VDIR) cache_purge(tdvp); if (fvp->v_type == VDIR && fdvp != tdvp) cache_purge(fdvp); } if (fdvp != rfdvp) vrele(fdvp); if (fvp != rfvp) vrele(fvp); if (ltdvp != NULLVP) VOP_UNLOCK(ltdvp, 0, td); if (tdvp != rtdvp) vrele(tdvp); if (ltvp != NULLVP) VOP_UNLOCK(ltvp, 0, td); if (tvp != rtvp && tvp != NULLVP) { if (rtvp == NULLVP) vput(tvp); else vrele(tvp); } UNIONFS_INTERNAL_DEBUG("unionfs_rename: leave (%d)\n", error); return (error); unionfs_rename_abort: if (fdvp != rfdvp) vrele(rfdvp); if (fvp != rfvp) vrele(rfvp); if (tdvp != rtdvp) vrele(rtdvp); vput(tdvp); if (tvp != rtvp && rtvp != NULLVP) vrele(rtvp); if (tvp != NULLVP) { if (tdvp != tvp) vput(tvp); else vrele(tvp); } vrele(fdvp); vrele(fvp); UNIONFS_INTERNAL_DEBUG("unionfs_rename: leave (%d)\n", error); return (error); } static int unionfs_mkdir(struct vop_mkdir_args *ap) { int error; int lkflags; struct unionfs_node *dunp; struct componentname *cnp; struct thread *td; struct vnode *udvp; struct vnode *uvp; struct vattr va; UNIONFS_INTERNAL_DEBUG("unionfs_mkdir: enter\n"); error = EROFS; dunp = VTOUNIONFS(ap->a_dvp); cnp = ap->a_cnp; lkflags = cnp->cn_lkflags; td = curthread; udvp = dunp->un_uppervp; if (udvp != NULLVP) { /* check opaque */ if (!(cnp->cn_flags & ISWHITEOUT)) { error = VOP_GETATTR(udvp, &va, cnp->cn_cred, td); if (error != 0) return (error); if (va.va_flags & OPAQUE) cnp->cn_flags |= ISWHITEOUT; } if ((error = VOP_MKDIR(udvp, &uvp, cnp, ap->a_vap)) == 0) { VOP_UNLOCK(uvp, 0, td); cnp->cn_lkflags = LK_EXCLUSIVE; error = unionfs_nodeget(ap->a_dvp->v_mount, uvp, NULLVP, ap->a_dvp, ap->a_vpp, cnp, td); cnp->cn_lkflags = lkflags; vrele(uvp); } } UNIONFS_INTERNAL_DEBUG("unionfs_mkdir: leave (%d)\n", error); return (error); } static int unionfs_rmdir(struct vop_rmdir_args *ap) { int error; struct unionfs_node *dunp; struct unionfs_node *unp; struct unionfs_mount *ump; struct componentname *cnp; struct thread *td; struct vnode *udvp; struct vnode *uvp; struct vnode *lvp; UNIONFS_INTERNAL_DEBUG("unionfs_rmdir: enter\n"); error = 0; dunp = VTOUNIONFS(ap->a_dvp); unp = VTOUNIONFS(ap->a_vp); cnp = ap->a_cnp; td = curthread; udvp = dunp->un_uppervp; uvp = unp->un_uppervp; lvp = unp->un_lowervp; if (udvp == NULLVP) return (EROFS); if (udvp == uvp) return (EOPNOTSUPP); if (uvp != NULLVP) { if (lvp != NULLVP) { error = unionfs_check_rmdir(ap->a_vp, cnp->cn_cred, td); if (error != 0) return (error); } ump = MOUNTTOUNIONFSMOUNT(ap->a_vp->v_mount); if (ump->um_whitemode == UNIONFS_WHITE_ALWAYS || lvp != NULLVP) cnp->cn_flags |= DOWHITEOUT; error = VOP_RMDIR(udvp, uvp, cnp); } else if (lvp != NULLVP) error = unionfs_mkwhiteout(udvp, cnp, td, unp->un_path); if (error == 0) { cache_purge(ap->a_dvp); cache_purge(ap->a_vp); } UNIONFS_INTERNAL_DEBUG("unionfs_rmdir: leave (%d)\n", error); return (error); } static int unionfs_symlink(struct vop_symlink_args *ap) { int error; int lkflags; struct unionfs_node *dunp; struct componentname *cnp; struct thread *td; struct vnode *udvp; struct vnode *uvp; UNIONFS_INTERNAL_DEBUG("unionfs_symlink: enter\n"); error = EROFS; dunp = VTOUNIONFS(ap->a_dvp); cnp = ap->a_cnp; lkflags = cnp->cn_lkflags; td = curthread; udvp = dunp->un_uppervp; if (udvp != NULLVP) { error = VOP_SYMLINK(udvp, &uvp, cnp, ap->a_vap, ap->a_target); if (error == 0) { VOP_UNLOCK(uvp, 0, td); cnp->cn_lkflags = LK_EXCLUSIVE; error = unionfs_nodeget(ap->a_dvp->v_mount, uvp, NULLVP, ap->a_dvp, ap->a_vpp, cnp, td); cnp->cn_lkflags = lkflags; vrele(uvp); } } UNIONFS_INTERNAL_DEBUG("unionfs_symlink: leave (%d)\n", error); return (error); } static int unionfs_readdir(struct vop_readdir_args *ap) { int error; int eofflag; int locked; struct unionfs_node *unp; struct unionfs_node_status *unsp; struct uio *uio; struct vnode *uvp; struct vnode *lvp; struct thread *td; struct vattr va; int ncookies_bk; u_long *cookies_bk; UNIONFS_INTERNAL_DEBUG("unionfs_readdir: enter\n"); error = 0; eofflag = 0; locked = 0; unp = VTOUNIONFS(ap->a_vp); uio = ap->a_uio; uvp = unp->un_uppervp; lvp = unp->un_lowervp; td = uio->uio_td; ncookies_bk = 0; cookies_bk = NULL; if (ap->a_vp->v_type != VDIR) return (ENOTDIR); /* check opaque */ if (uvp != NULLVP && lvp != NULLVP) { if ((error = VOP_GETATTR(uvp, &va, ap->a_cred, td)) != 0) goto unionfs_readdir_exit; if (va.va_flags & OPAQUE) lvp = NULLVP; } /* check the open count. unionfs needs to open before readdir. */ if (VOP_ISLOCKED(ap->a_vp, td) != LK_EXCLUSIVE) { vn_lock(ap->a_vp, LK_UPGRADE | LK_RETRY, td); locked = 1; } unionfs_get_node_status(unp, td, &unsp); if ((uvp != NULLVP && unsp->uns_upper_opencnt <= 0) || (lvp != NULLVP && unsp->uns_lower_opencnt <= 0)) { unionfs_tryrem_node_status(unp, td, unsp); error = EBADF; } if (locked == 1) vn_lock(ap->a_vp, LK_DOWNGRADE | LK_RETRY, td); if (error != 0) goto unionfs_readdir_exit; /* upper only */ if (uvp != NULLVP && lvp == NULLVP) { error = VOP_READDIR(uvp, uio, ap->a_cred, ap->a_eofflag, ap->a_ncookies, ap->a_cookies); unsp->uns_readdir_status = 0; goto unionfs_readdir_exit; } /* lower only */ if (uvp == NULLVP && lvp != NULLVP) { error = VOP_READDIR(lvp, uio, ap->a_cred, ap->a_eofflag, ap->a_ncookies, ap->a_cookies); unsp->uns_readdir_status = 2; goto unionfs_readdir_exit; } /* * readdir upper and lower */ KASSERT(uvp != NULLVP, ("unionfs_readdir: null upper vp")); KASSERT(lvp != NULLVP, ("unionfs_readdir: null lower vp")); if (uio->uio_offset == 0) unsp->uns_readdir_status = 0; if (unsp->uns_readdir_status == 0) { /* read upper */ error = VOP_READDIR(uvp, uio, ap->a_cred, &eofflag, ap->a_ncookies, ap->a_cookies); if (error != 0 || eofflag == 0) goto unionfs_readdir_exit; unsp->uns_readdir_status = 1; /* * ufs(and other fs) needs size of uio_resid larger than * DIRBLKSIZ. * size of DIRBLKSIZ equals DEV_BSIZE. * (see: ufs/ufs/ufs_vnops.c ufs_readdir func , ufs/ufs/dir.h) */ if (uio->uio_resid <= (uio->uio_resid & (DEV_BSIZE -1))) goto unionfs_readdir_exit; /* * backup cookies * It prepares to readdir in lower. */ if (ap->a_ncookies != NULL) { ncookies_bk = *(ap->a_ncookies); *(ap->a_ncookies) = 0; } if (ap->a_cookies != NULL) { cookies_bk = *(ap->a_cookies); *(ap->a_cookies) = NULL; } } /* initialize for readdir in lower */ if (unsp->uns_readdir_status == 1) { unsp->uns_readdir_status = 2; uio->uio_offset = 0; } if (lvp == NULLVP) { error = EBADF; goto unionfs_readdir_exit; } /* read lower */ error = VOP_READDIR(lvp, uio, ap->a_cred, ap->a_eofflag, ap->a_ncookies, ap->a_cookies); if (cookies_bk != NULL) { /* merge cookies */ int size; u_long *newcookies, *pos; size = *(ap->a_ncookies) + ncookies_bk; newcookies = (u_long *) malloc(size * sizeof(u_long), M_TEMP, M_WAITOK); pos = newcookies; memcpy(pos, cookies_bk, ncookies_bk * sizeof(u_long)); pos += ncookies_bk * sizeof(u_long); memcpy(pos, *(ap->a_cookies), *(ap->a_ncookies) * sizeof(u_long)); free(cookies_bk, M_TEMP); free(*(ap->a_cookies), M_TEMP); *(ap->a_ncookies) = size; *(ap->a_cookies) = newcookies; } unionfs_readdir_exit: if (error != 0 && ap->a_eofflag != NULL) *(ap->a_eofflag) = 1; UNIONFS_INTERNAL_DEBUG("unionfs_readdir: leave (%d)\n", error); return (error); } static int unionfs_readlink(struct vop_readlink_args *ap) { int error; struct unionfs_node *unp; struct vnode *vp; UNIONFS_INTERNAL_DEBUG("unionfs_readlink: enter\n"); unp = VTOUNIONFS(ap->a_vp); vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); error = VOP_READLINK(vp, ap->a_uio, ap->a_cred); UNIONFS_INTERNAL_DEBUG("unionfs_readlink: leave (%d)\n", error); return (error); } static int unionfs_getwritemount(struct vop_getwritemount_args *ap) { int error; struct vnode *uvp; struct vnode *vp; UNIONFS_INTERNAL_DEBUG("unionfs_getwritemount: enter\n"); error = 0; vp = ap->a_vp; if (vp == NULLVP || (vp->v_mount->mnt_flag & MNT_RDONLY)) return (EACCES); uvp = UNIONFSVPTOUPPERVP(vp); if (uvp == NULLVP && VREG == vp->v_type) uvp = UNIONFSVPTOUPPERVP(VTOUNIONFS(vp)->un_dvp); if (uvp != NULLVP) error = VOP_GETWRITEMOUNT(uvp, ap->a_mpp); else { VI_LOCK(vp); if (vp->v_iflag & VI_FREE) error = EOPNOTSUPP; else error = EACCES; VI_UNLOCK(vp); } UNIONFS_INTERNAL_DEBUG("unionfs_getwritemount: leave (%d)\n", error); return (error); } static int unionfs_inactive(struct vop_inactive_args *ap) { ap->a_vp->v_object = NULL; vrecycle(ap->a_vp, ap->a_td); return (0); } static int unionfs_reclaim(struct vop_reclaim_args *ap) { /* UNIONFS_INTERNAL_DEBUG("unionfs_reclaim: enter\n"); */ unionfs_noderem(ap->a_vp, ap->a_td); /* UNIONFS_INTERNAL_DEBUG("unionfs_reclaim: leave\n"); */ return (0); } static int unionfs_print(struct vop_print_args *ap) { struct unionfs_node *unp; /* struct unionfs_node_status *unsp; */ unp = VTOUNIONFS(ap->a_vp); /* unionfs_get_node_status(unp, curthread, &unsp); */ printf("unionfs_vp=%p, uppervp=%p, lowervp=%p\n", ap->a_vp, unp->un_uppervp, unp->un_lowervp); /* printf("unionfs opencnt: uppervp=%d, lowervp=%d\n", unsp->uns_upper_opencnt, unsp->uns_lower_opencnt); */ if (unp->un_uppervp != NULLVP) vprint("unionfs: upper", unp->un_uppervp); if (unp->un_lowervp != NULLVP) vprint("unionfs: lower", unp->un_lowervp); return (0); } static int unionfs_get_llt_revlock(int flags) { int count; flags &= LK_TYPE_MASK; for (count = 0; un_llt[count].lock != 0; count++) { if (flags == un_llt[count].lock) { return un_llt[count].revlock; } } return 0; } static int unionfs_lock(struct vop_lock1_args *ap) { int error; int flags; int revlock; int uhold; struct mount *mp; struct unionfs_mount *ump; struct unionfs_node *unp; struct vnode *vp; struct vnode *uvp; struct vnode *lvp; struct thread *td; error = 0; uhold = 0; flags = ap->a_flags; vp = ap->a_vp; td = ap->a_td; if (LK_RELEASE == (flags & LK_TYPE_MASK) || !(flags & LK_TYPE_MASK)) return (VOP_UNLOCK(vp, flags, td)); if ((revlock = unionfs_get_llt_revlock(flags)) == 0) panic("unknown lock type: 0x%x", flags & LK_TYPE_MASK); if ((flags & LK_INTERLOCK) == 0) VI_LOCK(vp); mp = vp->v_mount; if (mp == NULL) goto unionfs_lock_null_vnode; ump = MOUNTTOUNIONFSMOUNT(mp); unp = VTOUNIONFS(vp); if (ump == NULL || unp == NULL) goto unionfs_lock_null_vnode; lvp = unp->un_lowervp; uvp = unp->un_uppervp; if ((mp->mnt_kern_flag & MNTK_MPSAFE) != 0 && (vp->v_iflag & VI_OWEINACT) != 0) flags |= LK_NOWAIT; /* * Sometimes, lower or upper is already exclusive locked. * (ex. vfs_domount: mounted vnode is already locked.) */ if ((flags & LK_TYPE_MASK) == LK_EXCLUSIVE && vp == ump->um_rootvp) flags |= LK_CANRECURSE; if (lvp != NULLVP) { VI_LOCK_FLAGS(lvp, MTX_DUPOK); flags |= LK_INTERLOCK; vholdl(lvp); VI_UNLOCK(vp); ap->a_flags &= ~LK_INTERLOCK; error = VOP_LOCK(lvp, flags, td); VI_LOCK(vp); unp = VTOUNIONFS(vp); if (unp == NULL) { VI_UNLOCK(vp); if (error == 0) VOP_UNLOCK(lvp, 0, td); vdrop(lvp); return (vop_stdlock(ap)); } } if (error == 0 && uvp != NULLVP) { VI_LOCK_FLAGS(uvp, MTX_DUPOK); flags |= LK_INTERLOCK; vholdl(uvp); uhold = 1; VI_UNLOCK(vp); ap->a_flags &= ~LK_INTERLOCK; error = VOP_LOCK(uvp, flags, td); VI_LOCK(vp); unp = VTOUNIONFS(vp); if (unp == NULL) { VI_UNLOCK(vp); if (error == 0) { VOP_UNLOCK(uvp, 0, td); if (lvp != NULLVP) VOP_UNLOCK(lvp, 0, td); } if (lvp != NULLVP) vdrop(lvp); vdrop(uvp); return (vop_stdlock(ap)); } if (error != 0 && lvp != NULLVP) { VI_UNLOCK(vp); if ((revlock & LK_TYPE_MASK) == LK_RELEASE) VOP_UNLOCK(lvp, revlock, td); else vn_lock(lvp, revlock | LK_RETRY, td); goto unionfs_lock_abort; } } VI_UNLOCK(vp); unionfs_lock_abort: if (lvp != NULLVP) vdrop(lvp); if (uhold != 0) vdrop(uvp); return (error); unionfs_lock_null_vnode: ap->a_flags |= LK_INTERLOCK; return (vop_stdlock(ap)); } static int unionfs_unlock(struct vop_unlock_args *ap) { int error; int flags; int mtxlkflag; int uhold; struct vnode *vp; struct vnode *lvp; struct vnode *uvp; struct unionfs_node *unp; error = 0; mtxlkflag = 0; uhold = 0; flags = ap->a_flags | LK_RELEASE; vp = ap->a_vp; if ((flags & LK_INTERLOCK) != 0) mtxlkflag = 1; else if (mtx_owned(VI_MTX(vp)) == 0) { VI_LOCK(vp); mtxlkflag = 2; } unp = VTOUNIONFS(vp); if (unp == NULL) goto unionfs_unlock_null_vnode; lvp = unp->un_lowervp; uvp = unp->un_uppervp; if (lvp != NULLVP) { VI_LOCK_FLAGS(lvp, MTX_DUPOK); flags |= LK_INTERLOCK; vholdl(lvp); VI_UNLOCK(vp); ap->a_flags &= ~LK_INTERLOCK; error = VOP_UNLOCK(lvp, flags, ap->a_td); VI_LOCK(vp); } if (error == 0 && uvp != NULLVP) { VI_LOCK_FLAGS(uvp, MTX_DUPOK); flags |= LK_INTERLOCK; vholdl(uvp); uhold = 1; VI_UNLOCK(vp); ap->a_flags &= ~LK_INTERLOCK; error = VOP_UNLOCK(uvp, flags, ap->a_td); VI_LOCK(vp); } VI_UNLOCK(vp); if (lvp != NULLVP) vdrop(lvp); if (uhold != 0) vdrop(uvp); if (mtxlkflag == 0) VI_LOCK(vp); return error; unionfs_unlock_null_vnode: if (mtxlkflag == 2) VI_UNLOCK(vp); return (vop_stdunlock(ap)); } static int unionfs_pathconf(struct vop_pathconf_args *ap) { struct unionfs_node *unp; struct vnode *vp; unp = VTOUNIONFS(ap->a_vp); vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); return (VOP_PATHCONF(vp, ap->a_name, ap->a_retval)); } static int unionfs_advlock(struct vop_advlock_args *ap) { int error; struct unionfs_node *unp; struct unionfs_node_status *unsp; struct vnode *vp; struct vnode *uvp; struct thread *td; UNIONFS_INTERNAL_DEBUG("unionfs_advlock: enter\n"); vp = ap->a_vp; td = curthread; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); unp = VTOUNIONFS(ap->a_vp); uvp = unp->un_uppervp; if (uvp == NULLVP) { error = unionfs_copyfile(unp, 1, td->td_ucred, td); if (error != 0) goto unionfs_advlock_abort; uvp = unp->un_uppervp; unionfs_get_node_status(unp, td, &unsp); if (unsp->uns_lower_opencnt > 0) { /* try reopen the vnode */ error = VOP_OPEN(uvp, unsp->uns_lower_openmode, td->td_ucred, td, NULL); if (error) goto unionfs_advlock_abort; unsp->uns_upper_opencnt++; VOP_CLOSE(unp->un_lowervp, unsp->uns_lower_openmode, td->td_ucred, td); unsp->uns_lower_opencnt--; } else unionfs_tryrem_node_status(unp, td, unsp); } VOP_UNLOCK(vp, 0, td); error = VOP_ADVLOCK(uvp, ap->a_id, ap->a_op, ap->a_fl, ap->a_flags); UNIONFS_INTERNAL_DEBUG("unionfs_advlock: leave (%d)\n", error); return error; unionfs_advlock_abort: VOP_UNLOCK(vp, 0, td); UNIONFS_INTERNAL_DEBUG("unionfs_advlock: leave (%d)\n", error); return error; } static int unionfs_strategy(struct vop_strategy_args *ap) { struct unionfs_node *unp; struct vnode *vp; unp = VTOUNIONFS(ap->a_vp); vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); #ifdef DIAGNOSTIC if (vp == NULLVP) panic("unionfs_strategy: nullvp"); if (ap->a_bp->b_iocmd == BIO_WRITE && vp == unp->un_lowervp) panic("unionfs_strategy: writing to lowervp"); #endif return (VOP_STRATEGY(vp, ap->a_bp)); } static int unionfs_getacl(struct vop_getacl_args *ap) { int error; struct unionfs_node *unp; struct vnode *vp; unp = VTOUNIONFS(ap->a_vp); vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); UNIONFS_INTERNAL_DEBUG("unionfs_getacl: enter\n"); error = VOP_GETACL(vp, ap->a_type, ap->a_aclp, ap->a_cred, ap->a_td); UNIONFS_INTERNAL_DEBUG("unionfs_getacl: leave (%d)\n", error); return (error); } static int unionfs_setacl(struct vop_setacl_args *ap) { int error; struct unionfs_node *unp; struct vnode *uvp; struct vnode *lvp; struct thread *td; UNIONFS_INTERNAL_DEBUG("unionfs_setacl: enter\n"); error = EROFS; unp = VTOUNIONFS(ap->a_vp); uvp = unp->un_uppervp; lvp = unp->un_lowervp; td = ap->a_td; if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (uvp == NULLVP && lvp->v_type == VREG) { if ((error = unionfs_copyfile(unp, 1, ap->a_cred, td)) != 0) return (error); uvp = unp->un_uppervp; } if (uvp != NULLVP) error = VOP_SETACL(uvp, ap->a_type, ap->a_aclp, ap->a_cred, td); UNIONFS_INTERNAL_DEBUG("unionfs_setacl: leave (%d)\n", error); return (error); } static int unionfs_aclcheck(struct vop_aclcheck_args *ap) { int error; struct unionfs_node *unp; struct vnode *vp; UNIONFS_INTERNAL_DEBUG("unionfs_aclcheck: enter\n"); unp = VTOUNIONFS(ap->a_vp); vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); error = VOP_ACLCHECK(vp, ap->a_type, ap->a_aclp, ap->a_cred, ap->a_td); UNIONFS_INTERNAL_DEBUG("unionfs_aclcheck: leave (%d)\n", error); return (error); } static int unionfs_openextattr(struct vop_openextattr_args *ap) { int error; struct unionfs_node *unp; struct vnode *vp; struct vnode *tvp; vp = ap->a_vp; unp = VTOUNIONFS(vp); tvp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); if ((tvp == unp->un_uppervp && (unp->un_flag & UNIONFS_OPENEXTU)) || (tvp == unp->un_lowervp && (unp->un_flag & UNIONFS_OPENEXTL))) return (EBUSY); error = VOP_OPENEXTATTR(tvp, ap->a_cred, ap->a_td); if (error == 0) { vn_lock(vp, LK_UPGRADE | LK_RETRY, ap->a_td); if (tvp == unp->un_uppervp) unp->un_flag |= UNIONFS_OPENEXTU; else unp->un_flag |= UNIONFS_OPENEXTL; vn_lock(vp, LK_DOWNGRADE | LK_RETRY, ap->a_td); } return (error); } static int unionfs_closeextattr(struct vop_closeextattr_args *ap) { int error; struct unionfs_node *unp; struct vnode *vp; struct vnode *tvp; vp = ap->a_vp; unp = VTOUNIONFS(vp); tvp = NULLVP; if (unp->un_flag & UNIONFS_OPENEXTU) tvp = unp->un_uppervp; else if (unp->un_flag & UNIONFS_OPENEXTL) tvp = unp->un_lowervp; if (tvp == NULLVP) return (EOPNOTSUPP); error = VOP_CLOSEEXTATTR(tvp, ap->a_commit, ap->a_cred, ap->a_td); if (error == 0) { vn_lock(vp, LK_UPGRADE | LK_RETRY, ap->a_td); if (tvp == unp->un_uppervp) unp->un_flag &= ~UNIONFS_OPENEXTU; else unp->un_flag &= ~UNIONFS_OPENEXTL; vn_lock(vp, LK_DOWNGRADE | LK_RETRY, ap->a_td); } return (error); } static int unionfs_getextattr(struct vop_getextattr_args *ap) { struct unionfs_node *unp; struct vnode *vp; unp = VTOUNIONFS(ap->a_vp); vp = NULLVP; if (unp->un_flag & UNIONFS_OPENEXTU) vp = unp->un_uppervp; else if (unp->un_flag & UNIONFS_OPENEXTL) vp = unp->un_lowervp; if (vp == NULLVP) return (EOPNOTSUPP); return (VOP_GETEXTATTR(vp, ap->a_attrnamespace, ap->a_name, ap->a_uio, ap->a_size, ap->a_cred, ap->a_td)); } static int unionfs_setextattr(struct vop_setextattr_args *ap) { int error; struct unionfs_node *unp; struct vnode *uvp; struct vnode *lvp; struct vnode *ovp; struct ucred *cred; struct thread *td; error = EROFS; unp = VTOUNIONFS(ap->a_vp); uvp = unp->un_uppervp; lvp = unp->un_lowervp; ovp = NULLVP; cred = ap->a_cred; td = ap->a_td; UNIONFS_INTERNAL_DEBUG("unionfs_setextattr: enter (un_flag=%x)\n", unp->un_flag); if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (unp->un_flag & UNIONFS_OPENEXTU) ovp = unp->un_uppervp; else if (unp->un_flag & UNIONFS_OPENEXTL) ovp = unp->un_lowervp; if (ovp == NULLVP) return (EOPNOTSUPP); if (ovp == lvp && lvp->v_type == VREG) { VOP_CLOSEEXTATTR(lvp, 0, cred, td); if (uvp == NULLVP && (error = unionfs_copyfile(unp, 1, cred, td)) != 0) { unionfs_setextattr_reopen: if ((unp->un_flag & UNIONFS_OPENEXTL) && VOP_OPENEXTATTR(lvp, cred, td)) { #ifdef DIAGNOSTIC panic("unionfs: VOP_OPENEXTATTR failed"); #endif unp->un_flag &= ~UNIONFS_OPENEXTL; } goto unionfs_setextattr_abort; } uvp = unp->un_uppervp; if ((error = VOP_OPENEXTATTR(uvp, cred, td)) != 0) goto unionfs_setextattr_reopen; unp->un_flag &= ~UNIONFS_OPENEXTL; unp->un_flag |= UNIONFS_OPENEXTU; ovp = uvp; } if (ovp == uvp) error = VOP_SETEXTATTR(ovp, ap->a_attrnamespace, ap->a_name, ap->a_uio, cred, td); unionfs_setextattr_abort: UNIONFS_INTERNAL_DEBUG("unionfs_setextattr: leave (%d)\n", error); return (error); } static int unionfs_listextattr(struct vop_listextattr_args *ap) { struct unionfs_node *unp; struct vnode *vp; unp = VTOUNIONFS(ap->a_vp); vp = NULLVP; if (unp->un_flag & UNIONFS_OPENEXTU) vp = unp->un_uppervp; else if (unp->un_flag & UNIONFS_OPENEXTL) vp = unp->un_lowervp; if (vp == NULLVP) return (EOPNOTSUPP); return (VOP_LISTEXTATTR(vp, ap->a_attrnamespace, ap->a_uio, ap->a_size, ap->a_cred, ap->a_td)); } static int unionfs_deleteextattr(struct vop_deleteextattr_args *ap) { int error; struct unionfs_node *unp; struct vnode *uvp; struct vnode *lvp; struct vnode *ovp; struct ucred *cred; struct thread *td; error = EROFS; unp = VTOUNIONFS(ap->a_vp); uvp = unp->un_uppervp; lvp = unp->un_lowervp; ovp = NULLVP; cred = ap->a_cred; td = ap->a_td; UNIONFS_INTERNAL_DEBUG("unionfs_deleteextattr: enter (un_flag=%x)\n", unp->un_flag); if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (unp->un_flag & UNIONFS_OPENEXTU) ovp = unp->un_uppervp; else if (unp->un_flag & UNIONFS_OPENEXTL) ovp = unp->un_lowervp; if (ovp == NULLVP) return (EOPNOTSUPP); if (ovp == lvp && lvp->v_type == VREG) { VOP_CLOSEEXTATTR(lvp, 0, cred, td); if (uvp == NULLVP && (error = unionfs_copyfile(unp, 1, cred, td)) != 0) { unionfs_deleteextattr_reopen: if ((unp->un_flag & UNIONFS_OPENEXTL) && VOP_OPENEXTATTR(lvp, cred, td)) { #ifdef DIAGNOSTIC panic("unionfs: VOP_OPENEXTATTR failed"); #endif unp->un_flag &= ~UNIONFS_OPENEXTL; } goto unionfs_deleteextattr_abort; } uvp = unp->un_uppervp; if ((error = VOP_OPENEXTATTR(uvp, cred, td)) != 0) goto unionfs_deleteextattr_reopen; unp->un_flag &= ~UNIONFS_OPENEXTL; unp->un_flag |= UNIONFS_OPENEXTU; ovp = uvp; } if (ovp == uvp) error = VOP_DELETEEXTATTR(ovp, ap->a_attrnamespace, ap->a_name, ap->a_cred, ap->a_td); unionfs_deleteextattr_abort: UNIONFS_INTERNAL_DEBUG("unionfs_deleteextattr: leave (%d)\n", error); return (error); } static int unionfs_setlabel(struct vop_setlabel_args *ap) { int error; struct unionfs_node *unp; struct vnode *uvp; struct vnode *lvp; struct thread *td; UNIONFS_INTERNAL_DEBUG("unionfs_setlabel: enter\n"); error = EROFS; unp = VTOUNIONFS(ap->a_vp); uvp = unp->un_uppervp; lvp = unp->un_lowervp; td = ap->a_td; if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (uvp == NULLVP && lvp->v_type == VREG) { if ((error = unionfs_copyfile(unp, 1, ap->a_cred, td)) != 0) return (error); uvp = unp->un_uppervp; } if (uvp != NULLVP) error = VOP_SETLABEL(uvp, ap->a_label, ap->a_cred, td); UNIONFS_INTERNAL_DEBUG("unionfs_setlabel: leave (%d)\n", error); return (error); } static int unionfs_vptofh(struct vop_vptofh_args *ap) { return (EOPNOTSUPP); } struct vop_vector unionfs_vnodeops = { .vop_default = &default_vnodeops, .vop_access = unionfs_access, .vop_aclcheck = unionfs_aclcheck, .vop_advlock = unionfs_advlock, .vop_bmap = VOP_EOPNOTSUPP, .vop_cachedlookup = unionfs_lookup, .vop_close = unionfs_close, .vop_closeextattr = unionfs_closeextattr, .vop_create = unionfs_create, .vop_deleteextattr = unionfs_deleteextattr, .vop_fsync = unionfs_fsync, .vop_getacl = unionfs_getacl, .vop_getattr = unionfs_getattr, .vop_getextattr = unionfs_getextattr, .vop_getwritemount = unionfs_getwritemount, .vop_inactive = unionfs_inactive, .vop_ioctl = unionfs_ioctl, .vop_lease = unionfs_lease, .vop_link = unionfs_link, .vop_listextattr = unionfs_listextattr, .vop_lock1 = unionfs_lock, .vop_lookup = vfs_cache_lookup, .vop_mkdir = unionfs_mkdir, .vop_mknod = unionfs_mknod, .vop_open = unionfs_open, .vop_openextattr = unionfs_openextattr, .vop_pathconf = unionfs_pathconf, .vop_poll = unionfs_poll, .vop_print = unionfs_print, .vop_read = unionfs_read, .vop_readdir = unionfs_readdir, .vop_readlink = unionfs_readlink, .vop_reclaim = unionfs_reclaim, .vop_remove = unionfs_remove, .vop_rename = unionfs_rename, .vop_rmdir = unionfs_rmdir, .vop_setacl = unionfs_setacl, .vop_setattr = unionfs_setattr, .vop_setextattr = unionfs_setextattr, .vop_setlabel = unionfs_setlabel, .vop_strategy = unionfs_strategy, .vop_symlink = unionfs_symlink, .vop_unlock = unionfs_unlock, .vop_whiteout = unionfs_whiteout, .vop_write = unionfs_write, .vop_vptofh = unionfs_vptofh, }; Index: head/sys/kern/kern_lock.c =================================================================== --- head/sys/kern/kern_lock.c (revision 174950) +++ head/sys/kern/kern_lock.c (revision 174951) @@ -1,706 +1,693 @@ /*- * Copyright (c) 1995 * The Regents of the University of California. All rights reserved. * * Copyright (C) 1997 * John S. Dyson. All rights reserved. * * This code contains ideas from software contributed to Berkeley by * Avadis Tevanian, Jr., Michael Wayne Young, and the Mach Operating * System project at Carnegie-Mellon University. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. 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. * * @(#)kern_lock.c 8.18 (Berkeley) 5/21/95 */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include "opt_global.h" #include #include #include #include #include #include #include #include #include #include #ifdef DEBUG_LOCKS #include #endif static void assert_lockmgr(struct lock_object *lock, int what); #ifdef DDB #include static void db_show_lockmgr(struct lock_object *lock); #endif static void lock_lockmgr(struct lock_object *lock, int how); static int unlock_lockmgr(struct lock_object *lock); struct lock_class lock_class_lockmgr = { .lc_name = "lockmgr", .lc_flags = LC_SLEEPLOCK | LC_SLEEPABLE | LC_RECURSABLE | LC_UPGRADABLE, .lc_assert = assert_lockmgr, #ifdef DDB .lc_ddb_show = db_show_lockmgr, #endif .lc_lock = lock_lockmgr, .lc_unlock = unlock_lockmgr, }; /* * Locking primitives implementation. * Locks provide shared/exclusive sychronization. */ void assert_lockmgr(struct lock_object *lock, int what) { panic("lockmgr locks do not support assertions"); } void lock_lockmgr(struct lock_object *lock, int how) { panic("lockmgr locks do not support sleep interlocking"); } int unlock_lockmgr(struct lock_object *lock) { panic("lockmgr locks do not support sleep interlocking"); } #define COUNT(td, x) if ((td)) (td)->td_locks += (x) #define LK_ALL (LK_HAVE_EXCL | LK_WANT_EXCL | LK_WANT_UPGRADE | \ LK_SHARE_NONZERO | LK_WAIT_NONZERO) static int acquire(struct lock **lkpp, int extflags, int wanted, int *contested, uint64_t *waittime); static int acquiredrain(struct lock *lkp, int extflags) ; static __inline void sharelock(struct thread *td, struct lock *lkp, int incr) { lkp->lk_flags |= LK_SHARE_NONZERO; lkp->lk_sharecount += incr; COUNT(td, incr); } static __inline void shareunlock(struct thread *td, struct lock *lkp, int decr) { KASSERT(lkp->lk_sharecount >= decr, ("shareunlock: count < decr")); COUNT(td, -decr); if (lkp->lk_sharecount == decr) { lkp->lk_flags &= ~LK_SHARE_NONZERO; if (lkp->lk_flags & (LK_WANT_UPGRADE | LK_WANT_EXCL)) { wakeup(lkp); } lkp->lk_sharecount = 0; } else { lkp->lk_sharecount -= decr; } } static int acquire(struct lock **lkpp, int extflags, int wanted, int *contested, uint64_t *waittime) { struct lock *lkp = *lkpp; int error; CTR3(KTR_LOCK, "acquire(): lkp == %p, extflags == 0x%x, wanted == 0x%x", lkp, extflags, wanted); if ((extflags & LK_NOWAIT) && (lkp->lk_flags & wanted)) return EBUSY; error = 0; if ((lkp->lk_flags & wanted) != 0) lock_profile_obtain_lock_failed(&lkp->lk_object, contested, waittime); while ((lkp->lk_flags & wanted) != 0) { CTR2(KTR_LOCK, "acquire(): lkp == %p, lk_flags == 0x%x sleeping", lkp, lkp->lk_flags); lkp->lk_flags |= LK_WAIT_NONZERO; lkp->lk_waitcount++; error = msleep(lkp, lkp->lk_interlock, lkp->lk_prio, lkp->lk_wmesg, ((extflags & LK_TIMELOCK) ? lkp->lk_timo : 0)); lkp->lk_waitcount--; if (lkp->lk_waitcount == 0) lkp->lk_flags &= ~LK_WAIT_NONZERO; if (error) break; if (extflags & LK_SLEEPFAIL) { error = ENOLCK; break; } if (lkp->lk_newlock != NULL) { mtx_lock(lkp->lk_newlock->lk_interlock); mtx_unlock(lkp->lk_interlock); if (lkp->lk_waitcount == 0) wakeup((void *)(&lkp->lk_newlock)); *lkpp = lkp = lkp->lk_newlock; } } mtx_assert(lkp->lk_interlock, MA_OWNED); return (error); } /* * Set, change, or release a lock. * * Shared requests increment the shared count. Exclusive requests set the * LK_WANT_EXCL flag (preventing further shared locks), and wait for already * accepted shared locks and shared-to-exclusive upgrades to go away. */ int _lockmgr(struct lock *lkp, u_int flags, struct mtx *interlkp, struct thread *td, char *file, int line) { int error; struct thread *thr; int extflags, lockflags; int contested = 0; uint64_t waitstart = 0; /* * Lock owner can only be curthread or, at least, NULL in order to * have a deadlock free implementation of the primitive. */ KASSERT(td == NULL || td == curthread, ("lockmgr: owner thread (%p) cannot differ from curthread or NULL", td)); error = 0; if (td == NULL) thr = LK_KERNPROC; else thr = td; if ((flags & LK_INTERNAL) == 0) mtx_lock(lkp->lk_interlock); CTR6(KTR_LOCK, "lockmgr(): lkp == %p (lk_wmesg == \"%s\"), owner == %p, exclusivecount == %d, flags == 0x%x, " "td == %p", lkp, lkp->lk_wmesg, lkp->lk_lockholder, lkp->lk_exclusivecount, flags, td); #ifdef DEBUG_LOCKS { struct stack stack; /* XXX */ stack_save(&stack); CTRSTACK(KTR_LOCK, &stack, 0, 1); } #endif if (flags & LK_INTERLOCK) { mtx_assert(interlkp, MA_OWNED | MA_NOTRECURSED); mtx_unlock(interlkp); } if ((flags & (LK_NOWAIT|LK_RELEASE)) == 0) WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &lkp->lk_interlock->lock_object, "Acquiring lockmgr lock \"%s\"", lkp->lk_wmesg); if (panicstr != NULL) { mtx_unlock(lkp->lk_interlock); return (0); } if ((lkp->lk_flags & LK_NOSHARE) && (flags & LK_TYPE_MASK) == LK_SHARED) { flags &= ~LK_TYPE_MASK; flags |= LK_EXCLUSIVE; } extflags = (flags | lkp->lk_flags) & LK_EXTFLG_MASK; switch (flags & LK_TYPE_MASK) { case LK_SHARED: /* * If we are not the exclusive lock holder, we have to block * while there is an exclusive lock holder or while an * exclusive lock request or upgrade request is in progress. * * However, if TDP_DEADLKTREAT is set, we override exclusive * lock requests or upgrade requests ( but not the exclusive * lock itself ). */ if (lkp->lk_lockholder != thr) { lockflags = LK_HAVE_EXCL; if (td != NULL && !(td->td_pflags & TDP_DEADLKTREAT)) lockflags |= LK_WANT_EXCL | LK_WANT_UPGRADE; error = acquire(&lkp, extflags, lockflags, &contested, &waitstart); if (error) break; sharelock(td, lkp, 1); if (lkp->lk_sharecount == 1) lock_profile_obtain_lock_success(&lkp->lk_object, contested, waitstart, file, line); #if defined(DEBUG_LOCKS) stack_save(&lkp->lk_stack); #endif break; } /* * We hold an exclusive lock, so downgrade it to shared. * An alternative would be to fail with EDEADLK. */ sharelock(td, lkp, 1); if (lkp->lk_sharecount == 1) lock_profile_obtain_lock_success(&lkp->lk_object, contested, waitstart, file, line); /* FALLTHROUGH downgrade */ case LK_DOWNGRADE: KASSERT(lkp->lk_lockholder == thr && lkp->lk_exclusivecount != 0, ("lockmgr: not holding exclusive lock " "(owner thread (%p) != thread (%p), exlcnt (%d) != 0", lkp->lk_lockholder, thr, lkp->lk_exclusivecount)); sharelock(td, lkp, lkp->lk_exclusivecount); COUNT(td, -lkp->lk_exclusivecount); lkp->lk_exclusivecount = 0; lkp->lk_flags &= ~LK_HAVE_EXCL; lkp->lk_lockholder = LK_NOPROC; if (lkp->lk_waitcount) wakeup((void *)lkp); break; - case LK_EXCLUPGRADE: - /* - * If another process is ahead of us to get an upgrade, - * then we want to fail rather than have an intervening - * exclusive access. - */ - if (lkp->lk_flags & LK_WANT_UPGRADE) { - shareunlock(td, lkp, 1); - error = EBUSY; - break; - } - /* FALLTHROUGH normal upgrade */ - case LK_UPGRADE: /* * Upgrade a shared lock to an exclusive one. If another * shared lock has already requested an upgrade to an * exclusive lock, our shared lock is released and an * exclusive lock is requested (which will be granted * after the upgrade). If we return an error, the file * will always be unlocked. */ if (lkp->lk_lockholder == thr) panic("lockmgr: upgrade exclusive lock"); if (lkp->lk_sharecount <= 0) panic("lockmgr: upgrade without shared"); shareunlock(td, lkp, 1); if (lkp->lk_sharecount == 0) lock_profile_release_lock(&lkp->lk_object); /* * If we are just polling, check to see if we will block. */ if ((extflags & LK_NOWAIT) && ((lkp->lk_flags & LK_WANT_UPGRADE) || lkp->lk_sharecount > 1)) { error = EBUSY; break; } if ((lkp->lk_flags & LK_WANT_UPGRADE) == 0) { /* * We are first shared lock to request an upgrade, so * request upgrade and wait for the shared count to * drop to zero, then take exclusive lock. */ lkp->lk_flags |= LK_WANT_UPGRADE; error = acquire(&lkp, extflags, LK_SHARE_NONZERO, &contested, &waitstart); lkp->lk_flags &= ~LK_WANT_UPGRADE; if (error) { if ((lkp->lk_flags & ( LK_WANT_EXCL | LK_WAIT_NONZERO)) == (LK_WANT_EXCL | LK_WAIT_NONZERO)) wakeup((void *)lkp); break; } if (lkp->lk_exclusivecount != 0) panic("lockmgr: non-zero exclusive count"); lkp->lk_flags |= LK_HAVE_EXCL; lkp->lk_lockholder = thr; lkp->lk_exclusivecount = 1; COUNT(td, 1); lock_profile_obtain_lock_success(&lkp->lk_object, contested, waitstart, file, line); #if defined(DEBUG_LOCKS) stack_save(&lkp->lk_stack); #endif break; } /* * Someone else has requested upgrade. Release our shared * lock, awaken upgrade requestor if we are the last shared * lock, then request an exclusive lock. */ if ( (lkp->lk_flags & (LK_SHARE_NONZERO|LK_WAIT_NONZERO)) == LK_WAIT_NONZERO) wakeup((void *)lkp); /* FALLTHROUGH exclusive request */ case LK_EXCLUSIVE: if (lkp->lk_lockholder == thr && thr != LK_KERNPROC) { /* * Recursive lock. */ if ((extflags & (LK_NOWAIT | LK_CANRECURSE)) == 0) panic("lockmgr: locking against myself"); if ((extflags & LK_CANRECURSE) != 0) { lkp->lk_exclusivecount++; COUNT(td, 1); break; } } /* * If we are just polling, check to see if we will sleep. */ if ((extflags & LK_NOWAIT) && (lkp->lk_flags & (LK_HAVE_EXCL | LK_WANT_EXCL | LK_WANT_UPGRADE | LK_SHARE_NONZERO))) { error = EBUSY; break; } /* * Try to acquire the want_exclusive flag. */ error = acquire(&lkp, extflags, (LK_HAVE_EXCL | LK_WANT_EXCL), &contested, &waitstart); if (error) break; lkp->lk_flags |= LK_WANT_EXCL; /* * Wait for shared locks and upgrades to finish. */ error = acquire(&lkp, extflags, LK_HAVE_EXCL | LK_WANT_UPGRADE | LK_SHARE_NONZERO, &contested, &waitstart); lkp->lk_flags &= ~LK_WANT_EXCL; if (error) { if (lkp->lk_flags & LK_WAIT_NONZERO) wakeup((void *)lkp); break; } lkp->lk_flags |= LK_HAVE_EXCL; lkp->lk_lockholder = thr; if (lkp->lk_exclusivecount != 0) panic("lockmgr: non-zero exclusive count"); lkp->lk_exclusivecount = 1; COUNT(td, 1); lock_profile_obtain_lock_success(&lkp->lk_object, contested, waitstart, file, line); #if defined(DEBUG_LOCKS) stack_save(&lkp->lk_stack); #endif break; case LK_RELEASE: if (lkp->lk_exclusivecount != 0) { if (lkp->lk_lockholder != thr && lkp->lk_lockholder != LK_KERNPROC) { panic("lockmgr: thread %p, not %s %p unlocking", thr, "exclusive lock holder", lkp->lk_lockholder); } if (lkp->lk_lockholder != LK_KERNPROC) COUNT(td, -1); if (lkp->lk_exclusivecount == 1) { lkp->lk_flags &= ~LK_HAVE_EXCL; lkp->lk_lockholder = LK_NOPROC; lkp->lk_exclusivecount = 0; lock_profile_release_lock(&lkp->lk_object); } else { lkp->lk_exclusivecount--; } } else if (lkp->lk_flags & LK_SHARE_NONZERO) shareunlock(td, lkp, 1); else { printf("lockmgr: thread %p unlocking unheld lock\n", thr); kdb_backtrace(); } if (lkp->lk_flags & LK_WAIT_NONZERO) wakeup((void *)lkp); break; case LK_DRAIN: /* * Check that we do not already hold the lock, as it can * never drain if we do. Unfortunately, we have no way to * check for holding a shared lock, but at least we can * check for an exclusive one. */ if (lkp->lk_lockholder == thr) panic("lockmgr: draining against myself"); error = acquiredrain(lkp, extflags); if (error) break; lkp->lk_flags |= LK_DRAINING | LK_HAVE_EXCL; lkp->lk_lockholder = thr; lkp->lk_exclusivecount = 1; COUNT(td, 1); #if defined(DEBUG_LOCKS) stack_save(&lkp->lk_stack); #endif break; default: mtx_unlock(lkp->lk_interlock); panic("lockmgr: unknown locktype request %d", flags & LK_TYPE_MASK); /* NOTREACHED */ } if ((lkp->lk_flags & LK_WAITDRAIN) && (lkp->lk_flags & (LK_HAVE_EXCL | LK_WANT_EXCL | LK_WANT_UPGRADE | LK_SHARE_NONZERO | LK_WAIT_NONZERO)) == 0) { lkp->lk_flags &= ~LK_WAITDRAIN; wakeup((void *)&lkp->lk_flags); } mtx_unlock(lkp->lk_interlock); return (error); } static int acquiredrain(struct lock *lkp, int extflags) { int error; if ((extflags & LK_NOWAIT) && (lkp->lk_flags & LK_ALL)) { return EBUSY; } while (lkp->lk_flags & LK_ALL) { lkp->lk_flags |= LK_WAITDRAIN; error = msleep(&lkp->lk_flags, lkp->lk_interlock, lkp->lk_prio, lkp->lk_wmesg, ((extflags & LK_TIMELOCK) ? lkp->lk_timo : 0)); if (error) return error; if (extflags & LK_SLEEPFAIL) { return ENOLCK; } } return 0; } /* * Initialize a lock; required before use. */ void lockinit(lkp, prio, wmesg, timo, flags) struct lock *lkp; int prio; const char *wmesg; int timo; int flags; { CTR5(KTR_LOCK, "lockinit(): lkp == %p, prio == %d, wmesg == \"%s\", " "timo == %d, flags = 0x%x\n", lkp, prio, wmesg, timo, flags); lkp->lk_interlock = mtx_pool_alloc(mtxpool_lockbuilder); lkp->lk_flags = (flags & LK_EXTFLG_MASK); lkp->lk_sharecount = 0; lkp->lk_waitcount = 0; lkp->lk_exclusivecount = 0; lkp->lk_prio = prio; lkp->lk_timo = timo; lkp->lk_lockholder = LK_NOPROC; lkp->lk_newlock = NULL; #ifdef DEBUG_LOCKS stack_zero(&lkp->lk_stack); #endif lock_init(&lkp->lk_object, &lock_class_lockmgr, wmesg, NULL, LO_RECURSABLE | LO_SLEEPABLE | LO_UPGRADABLE); } /* * Destroy a lock. */ void lockdestroy(lkp) struct lock *lkp; { CTR2(KTR_LOCK, "lockdestroy(): lkp == %p (lk_wmesg == \"%s\")", lkp, lkp->lk_wmesg); lock_destroy(&lkp->lk_object); } /* * Determine the status of a lock. */ int lockstatus(lkp, td) struct lock *lkp; struct thread *td; { int lock_type = 0; int interlocked; if (!kdb_active) { interlocked = 1; mtx_lock(lkp->lk_interlock); } else interlocked = 0; if (lkp->lk_exclusivecount != 0) { if (td == NULL || lkp->lk_lockholder == td) lock_type = LK_EXCLUSIVE; else lock_type = LK_EXCLOTHER; } else if (lkp->lk_sharecount != 0) lock_type = LK_SHARED; if (interlocked) mtx_unlock(lkp->lk_interlock); return (lock_type); } /* * Determine the number of holders of a lock. */ int lockcount(lkp) struct lock *lkp; { int count; mtx_lock(lkp->lk_interlock); count = lkp->lk_exclusivecount + lkp->lk_sharecount; mtx_unlock(lkp->lk_interlock); return (count); } /* * Determine the number of waiters on a lock. */ int lockwaiters(lkp) struct lock *lkp; { int count; mtx_lock(lkp->lk_interlock); count = lkp->lk_waitcount; mtx_unlock(lkp->lk_interlock); return (count); } /* * Print out information about state of a lock. Used by VOP_PRINT * routines to display status about contained locks. */ void lockmgr_printinfo(lkp) struct lock *lkp; { if (lkp->lk_sharecount) printf(" lock type %s: SHARED (count %d)", lkp->lk_wmesg, lkp->lk_sharecount); else if (lkp->lk_flags & LK_HAVE_EXCL) printf(" lock type %s: EXCL (count %d) by thread %p (pid %d)", lkp->lk_wmesg, lkp->lk_exclusivecount, lkp->lk_lockholder, lkp->lk_lockholder->td_proc->p_pid); if (lkp->lk_waitcount > 0) printf(" with %d pending", lkp->lk_waitcount); #ifdef DEBUG_LOCKS stack_print_ddb(&lkp->lk_stack); #endif } #ifdef DDB /* * Check to see if a thread that is blocked on a sleep queue is actually * blocked on a 'struct lock'. If so, output some details and return true. * If the lock has an exclusive owner, return that in *ownerp. */ int lockmgr_chain(struct thread *td, struct thread **ownerp) { struct lock *lkp; lkp = td->td_wchan; /* Simple test to see if wchan points to a lockmgr lock. */ if (LOCK_CLASS(&lkp->lk_object) == &lock_class_lockmgr && lkp->lk_wmesg == td->td_wmesg) goto ok; /* * If this thread is doing a DRAIN, then it would be asleep on * &lkp->lk_flags rather than lkp. */ lkp = (struct lock *)((char *)td->td_wchan - offsetof(struct lock, lk_flags)); if (LOCK_CLASS(&lkp->lk_object) == &lock_class_lockmgr && lkp->lk_wmesg == td->td_wmesg && (lkp->lk_flags & LK_WAITDRAIN)) goto ok; /* Doen't seem to be a lockmgr lock. */ return (0); ok: /* Ok, we think we have a lockmgr lock, so output some details. */ db_printf("blocked on lk \"%s\" ", lkp->lk_wmesg); if (lkp->lk_sharecount) { db_printf("SHARED (count %d)\n", lkp->lk_sharecount); *ownerp = NULL; } else { db_printf("EXCL (count %d)\n", lkp->lk_exclusivecount); *ownerp = lkp->lk_lockholder; } return (1); } void db_show_lockmgr(struct lock_object *lock) { struct thread *td; struct lock *lkp; lkp = (struct lock *)lock; db_printf(" lock type: %s\n", lkp->lk_wmesg); db_printf(" state: "); if (lkp->lk_sharecount) db_printf("SHARED (count %d)\n", lkp->lk_sharecount); else if (lkp->lk_flags & LK_HAVE_EXCL) { td = lkp->lk_lockholder; db_printf("EXCL (count %d) %p ", lkp->lk_exclusivecount, td); db_printf("(tid %d, pid %d, \"%s\")\n", td->td_tid, td->td_proc->p_pid, td->td_name); } else db_printf("UNLOCKED\n"); if (lkp->lk_waitcount > 0) db_printf(" waiters: %d\n", lkp->lk_waitcount); } #endif Index: head/sys/sys/lockmgr.h =================================================================== --- head/sys/sys/lockmgr.h (revision 174950) +++ head/sys/sys/lockmgr.h (revision 174951) @@ -1,217 +1,210 @@ /*- * Copyright (c) 1995 * The Regents of the University of California. All rights reserved. * * This code contains ideas from software contributed to Berkeley by * Avadis Tevanian, Jr., Michael Wayne Young, and the Mach Operating * System project at Carnegie-Mellon University. * * 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. * 4. 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. * * @(#)lock.h 8.12 (Berkeley) 5/19/95 * $FreeBSD$ */ #ifndef _SYS_LOCKMGR_H_ #define _SYS_LOCKMGR_H_ #ifdef DEBUG_LOCKS #include /* XXX */ #endif #include #include struct mtx; /* * The general lock structure. Provides for multiple shared locks, * upgrading from shared to exclusive, and sleeping until the lock * can be gained. */ struct lock { struct lock_object lk_object; /* common lock properties */ struct mtx *lk_interlock; /* lock on remaining fields */ u_int lk_flags; /* see below */ int lk_sharecount; /* # of accepted shared locks */ int lk_waitcount; /* # of processes sleeping for lock */ short lk_exclusivecount; /* # of recursive exclusive locks */ short lk_prio; /* priority at which to sleep */ int lk_timo; /* maximum sleep time (for tsleep) */ struct thread *lk_lockholder; /* thread of exclusive lock holder */ struct lock *lk_newlock; /* lock taking over this lock */ #ifdef DEBUG_LOCKS struct stack lk_stack; #endif }; #define lk_wmesg lk_object.lo_name /* * Lock request types: * LK_SHARED - get one of many possible shared locks. If a process * holding an exclusive lock requests a shared lock, the exclusive * lock(s) will be downgraded to shared locks. * LK_EXCLUSIVE - stop further shared locks, when they are cleared, * grant a pending upgrade if it exists, then grant an exclusive * lock. Only one exclusive lock may exist at a time, except that * a process holding an exclusive lock may get additional exclusive * locks if it explicitly sets the LK_CANRECURSE flag in the lock * request, or if the LK_CANRECUSE flag was set when the lock was * initialized. * LK_UPGRADE - the process must hold a shared lock that it wants to * have upgraded to an exclusive lock. Other processes may get * exclusive access to the resource between the time that the upgrade * is requested and the time that it is granted. - * LK_EXCLUPGRADE - the process must hold a shared lock that it wants to - * have upgraded to an exclusive lock. If the request succeeds, no - * other processes will have gotten exclusive access to the resource - * between the time that the upgrade is requested and the time that - * it is granted. However, if another process has already requested - * an upgrade, the request will fail (see error returns below). * LK_DOWNGRADE - the process must hold an exclusive lock that it wants * to have downgraded to a shared lock. If the process holds multiple * (recursive) exclusive locks, they will all be downgraded to shared * locks. * LK_RELEASE - release one instance of a lock. * LK_DRAIN - wait for all activity on the lock to end, then mark it * decommissioned. This feature is used before freeing a lock that * is part of a piece of memory that is about to be freed. * LK_EXCLOTHER - return for lockstatus(). Used when another process * holds the lock exclusively. * * These are flags that are passed to the lockmgr routine. */ #define LK_TYPE_MASK 0x0000000f /* type of lock sought */ #define LK_SHARED 0x00000001 /* shared lock */ #define LK_EXCLUSIVE 0x00000002 /* exclusive lock */ #define LK_UPGRADE 0x00000003 /* shared-to-exclusive upgrade */ -#define LK_EXCLUPGRADE 0x00000004 /* first shared-to-exclusive upgrade */ #define LK_DOWNGRADE 0x00000005 /* exclusive-to-shared downgrade */ #define LK_RELEASE 0x00000006 /* release any type of lock */ #define LK_DRAIN 0x00000007 /* wait for all lock activity to end */ #define LK_EXCLOTHER 0x00000008 /* other process holds lock */ /* * External lock flags. * * These may be set in lock_init to set their mode permanently, * or passed in as arguments to the lock manager. */ #define LK_EXTFLG_MASK 0x00000ff0 /* mask of external flags */ #define LK_NOWAIT 0x00000010 /* do not sleep to await lock */ #define LK_SLEEPFAIL 0x00000020 /* sleep, then return failure */ #define LK_CANRECURSE 0x00000040 /* allow recursive exclusive lock */ #define LK_NOSHARE 0x00000080 /* Only allow exclusive locks */ #define LK_TIMELOCK 0x00000100 /* use lk_timo, else no timeout */ /* * Nonpersistent external flags. */ #define LK_RETRY 0x00001000 /* vn_lock: retry until locked */ #define LK_INTERLOCK 0x00002000 /* * unlock passed mutex after getting * lk_interlock */ /* * Internal lock flags. * * These flags are used internally to the lock manager. */ #define LK_WANT_UPGRADE 0x00010000 /* waiting for share-to-excl upgrade */ #define LK_WANT_EXCL 0x00020000 /* exclusive lock sought */ #define LK_HAVE_EXCL 0x00040000 /* exclusive lock obtained */ #define LK_WAITDRAIN 0x00080000 /* process waiting for lock to drain */ #define LK_DRAINING 0x00100000 /* lock is being drained */ #define LK_INTERNAL 0x00200000/* The internal lock is already held */ /* * Internal state flags corresponding to lk_sharecount, and lk_waitcount */ #define LK_SHARE_NONZERO 0x01000000 #define LK_WAIT_NONZERO 0x02000000 /* * Lock return status. * * Successfully obtained locks return 0. Locks will always succeed * unless one of the following is true: * LK_FORCEUPGRADE is requested and some other process has already * requested a lock upgrade (returns EBUSY). * LK_WAIT is set and a sleep would be required (returns EBUSY). * LK_SLEEPFAIL is set and a sleep was done (returns ENOLCK). * PCATCH is set in lock priority and a signal arrives (returns * either EINTR or ERESTART if system calls is to be restarted). * Non-null lock timeout and timeout expires (returns EWOULDBLOCK). * A failed lock attempt always returns a non-zero error value. No lock * is held after an error return (in particular, a failed LK_UPGRADE * or LK_FORCEUPGRADE will have released its shared access lock). */ /* * Indicator that no process holds exclusive lock */ #define LK_KERNPROC ((struct thread *)-2) #define LK_NOPROC ((struct thread *) -1) #ifdef INVARIANTS #define LOCKMGR_ASSERT(lkp, what, p) do { \ switch ((what)) { \ case LK_SHARED: \ if (lockstatus((lkp), (p)) == LK_SHARED) \ break; \ /* fall into exclusive */ \ case LK_EXCLUSIVE: \ if (lockstatus((lkp), (p)) != LK_EXCLUSIVE) \ panic("lock %s %s not held at %s:%d", \ (lkp)->lk_wmesg, #what, __FILE__, \ __LINE__); \ break; \ default: \ panic("unknown LOCKMGR_ASSERT at %s:%d", __FILE__, \ __LINE__); \ } \ } while (0) #else /* INVARIANTS */ #define LOCKMGR_ASSERT(lkp, p, what) #endif /* INVARIANTS */ void dumplockinfo(struct lock *lkp); struct thread; void lockinit(struct lock *, int prio, const char *wmesg, int timo, int flags); void lockdestroy(struct lock *); int _lockmgr(struct lock *, u_int flags, struct mtx *, struct thread *p, char *file, int line); void lockmgr_printinfo(struct lock *); int lockstatus(struct lock *, struct thread *); int lockcount(struct lock *); int lockwaiters(struct lock *); #define lockmgr(lock, flags, mtx, td) _lockmgr((lock), (flags), (mtx), (td), __FILE__, __LINE__) #ifdef DDB int lockmgr_chain(struct thread *td, struct thread **ownerp); #endif #endif /* !_SYS_LOCKMGR_H_ */ Index: head/sys/sys/param.h =================================================================== --- head/sys/sys/param.h (revision 174950) +++ head/sys/sys/param.h (revision 174951) @@ -1,308 +1,308 @@ /*- * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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. * 4. 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. * * @(#)param.h 8.3 (Berkeley) 4/4/95 * $FreeBSD$ */ #ifndef _SYS_PARAM_H_ #define _SYS_PARAM_H_ #include #define BSD 199506 /* System version (year & month). */ #define BSD4_3 1 #define BSD4_4 1 /* * __FreeBSD_version numbers are documented in the Porter's Handbook. * If you bump the version for any reason, you should update the documentation * there. * Currently this lives here: * * doc/en_US.ISO8859-1/books/porters-handbook/book.sgml * * scheme is: Rxx * 'R' is 0 if release branch or x.0-CURRENT before RELENG_*_0 * is created, otherwise 1. */ #undef __FreeBSD_version -#define __FreeBSD_version 800007 /* Master, propagated to newvers */ +#define __FreeBSD_version 800008 /* Master, propagated to newvers */ #ifndef LOCORE #include #endif /* * Machine-independent constants (some used in following include files). * Redefined constants are from POSIX 1003.1 limits file. * * MAXCOMLEN should be >= sizeof(ac_comm) (see ) * MAXLOGNAME should be == UT_NAMESIZE+1 (see ) */ #include #define MAXCOMLEN 19 /* max command name remembered */ #define MAXINTERP 32 /* max interpreter file name length */ #define MAXLOGNAME 17 /* max login name length (incl. NUL) */ #define MAXUPRC CHILD_MAX /* max simultaneous processes */ #define NCARGS ARG_MAX /* max bytes for an exec function */ #define NGROUPS NGROUPS_MAX /* max number groups */ #define NOFILE OPEN_MAX /* max open files per process */ #define NOGROUP 65535 /* marker for empty group set member */ #define MAXHOSTNAMELEN 256 /* max hostname size */ #define SPECNAMELEN 63 /* max length of devicename */ /* More types and definitions used throughout the kernel. */ #ifdef _KERNEL #include #include #ifndef LOCORE #include #include #endif #define FALSE 0 #define TRUE 1 #endif #ifndef _KERNEL /* Signals. */ #include #endif /* Machine type dependent parameters. */ #include #ifndef _KERNEL #include #endif #ifndef _NO_NAMESPACE_POLLUTION #ifndef DEV_BSHIFT #define DEV_BSHIFT 9 /* log2(DEV_BSIZE) */ #endif #define DEV_BSIZE (1<>PAGE_SHIFT) #endif /* * btodb() is messy and perhaps slow because `bytes' may be an off_t. We * want to shift an unsigned type to avoid sign extension and we don't * want to widen `bytes' unnecessarily. Assume that the result fits in * a daddr_t. */ #ifndef btodb #define btodb(bytes) /* calculates (bytes / DEV_BSIZE) */ \ (sizeof (bytes) > sizeof(long) \ ? (daddr_t)((unsigned long long)(bytes) >> DEV_BSHIFT) \ : (daddr_t)((unsigned long)(bytes) >> DEV_BSHIFT)) #endif #ifndef dbtob #define dbtob(db) /* calculates (db * DEV_BSIZE) */ \ ((off_t)(db) << DEV_BSHIFT) #endif #endif /* _NO_NAMESPACE_POLLUTION */ #define PRIMASK 0x0ff #define PCATCH 0x100 /* OR'd with pri for tsleep to check signals */ #define PDROP 0x200 /* OR'd with pri to stop re-entry of interlock mutex */ #define NZERO 0 /* default "nice" */ #define NBBY 8 /* number of bits in a byte */ #define NBPW sizeof(int) /* number of bytes per word (integer) */ #define CMASK 022 /* default file mask: S_IWGRP|S_IWOTH */ #define NODEV (dev_t)(-1) /* non-existent device */ #define CBLOCK 128 /* Clist block size, must be a power of 2. */ #define CBQSIZE (CBLOCK/NBBY) /* Quote bytes/cblock - can do better. */ /* Data chars/clist. */ #define CBSIZE (CBLOCK - sizeof(struct cblock *) - CBQSIZE) #define CROUND (CBLOCK - 1) /* Clist rounding. */ /* * File system parameters and macros. * * MAXBSIZE - Filesystems are made out of blocks of at most MAXBSIZE bytes * per block. MAXBSIZE may be made larger without effecting * any existing filesystems as long as it does not exceed MAXPHYS, * and may be made smaller at the risk of not being able to use * filesystems which require a block size exceeding MAXBSIZE. * * BKVASIZE - Nominal buffer space per buffer, in bytes. BKVASIZE is the * minimum KVM memory reservation the kernel is willing to make. * Filesystems can of course request smaller chunks. Actual * backing memory uses a chunk size of a page (PAGE_SIZE). * * If you make BKVASIZE too small you risk seriously fragmenting * the buffer KVM map which may slow things down a bit. If you * make it too big the kernel will not be able to optimally use * the KVM memory reserved for the buffer cache and will wind * up with too-few buffers. * * The default is 16384, roughly 2x the block size used by a * normal UFS filesystem. */ #define MAXBSIZE 65536 /* must be power of 2 */ #define BKVASIZE 16384 /* must be power of 2 */ #define BKVAMASK (BKVASIZE-1) /* * MAXPATHLEN defines the longest permissible path length after expanding * symbolic links. It is used to allocate a temporary buffer from the buffer * pool in which to do the name expansion, hence should be a power of two, * and must be less than or equal to MAXBSIZE. MAXSYMLINKS defines the * maximum number of symbolic links that may be expanded in a path name. * It should be set high enough to allow all legitimate uses, but halt * infinite loops reasonably quickly. */ #define MAXPATHLEN PATH_MAX #define MAXSYMLINKS 32 /* Bit map related macros. */ #define setbit(a,i) (((unsigned char *)(a))[(i)/NBBY] |= 1<<((i)%NBBY)) #define clrbit(a,i) (((unsigned char *)(a))[(i)/NBBY] &= ~(1<<((i)%NBBY))) #define isset(a,i) \ (((const unsigned char *)(a))[(i)/NBBY] & (1<<((i)%NBBY))) #define isclr(a,i) \ ((((const unsigned char *)(a))[(i)/NBBY] & (1<<((i)%NBBY))) == 0) /* Macros for counting and rounding. */ #ifndef howmany #define howmany(x, y) (((x)+((y)-1))/(y)) #endif #define rounddown(x, y) (((x)/(y))*(y)) #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* to any y */ #define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */ #define powerof2(x) ((((x)-1)&(x))==0) /* Macros for min/max. */ #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) #ifdef _KERNEL /* * Basic byte order function prototypes for non-inline functions. */ #ifndef LOCORE #ifndef _BYTEORDER_PROTOTYPED #define _BYTEORDER_PROTOTYPED __BEGIN_DECLS __uint32_t htonl(__uint32_t); __uint16_t htons(__uint16_t); __uint32_t ntohl(__uint32_t); __uint16_t ntohs(__uint16_t); __END_DECLS #endif #endif #ifndef lint #ifndef _BYTEORDER_FUNC_DEFINED #define _BYTEORDER_FUNC_DEFINED #define htonl(x) __htonl(x) #define htons(x) __htons(x) #define ntohl(x) __ntohl(x) #define ntohs(x) __ntohs(x) #endif /* !_BYTEORDER_FUNC_DEFINED */ #endif /* lint */ #endif /* _KERNEL */ /* * Scale factor for scaled integers used to count %cpu time and load avgs. * * The number of CPU `tick's that map to a unique `%age' can be expressed * by the formula (1 / (2 ^ (FSHIFT - 11))). The maximum load average that * can be calculated (assuming 32 bits) can be closely approximated using * the formula (2 ^ (2 * (16 - FSHIFT))) for (FSHIFT < 15). * * For the scheduler to maintain a 1:1 mapping of CPU `tick' to `%age', * FSHIFT must be at least 11; this gives us a maximum load avg of ~1024. */ #define FSHIFT 11 /* bits to right of fixed binary point */ #define FSCALE (1<> (PAGE_SHIFT - DEV_BSHIFT)) #define ctodb(db) /* calculates pages to devblks */ \ ((db) << (PAGE_SHIFT - DEV_BSHIFT)) #endif /* _SYS_PARAM_H_ */