sys/vfs/union/union_vnops.c
static int
union_link(struct vop_old_link_args *ap)
{
	struct componentname *cnp = ap->a_cnp;
	struct thread *td = cnp->cn_td;
	struct union_node *dun = VTOUNION(ap->a_tdvp);
	struct vnode *vp;
	struct vnode *tdvp;
	int error = 0;

	if (ap->a_tdvp->v_ops != ap->a_vp->v_ops) {
		vp = ap->a_vp;
	} else {
		struct union_node *tun = VTOUNION(ap->a_vp);

		if (tun->un_uppervp == NULLVP) {
			vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY);
#if 0
			if (dun->un_uppervp == tun->un_dirvp) {
				if (dun->un_flags & UN_ULOCK) {
					dun->un_flags &= ~UN_ULOCK;
					vn_unlock(dun->un_uppervp);
				}
			}
#endif
			error = union_copyup(tun, 1, cnp->cn_cred, td);
#if 0
			if (dun->un_uppervp == tun->un_dirvp) {
				vn_lock(dun->un_uppervp, 
					LK_EXCLUSIVE | LK_RETRY);
				dun->un_flags |= UN_ULOCK;
			}
#endif
			vn_unlock(ap->a_vp);
		}
		vp = tun->un_uppervp;
	}

	if (error)
		return (error);

	/*
	 * Make sure upper is locked, then unlock the union directory we were 
	 * called with to avoid a deadlock while we are calling VOP_LINK on 
	 * the upper (with tdvp locked and vp not locked).  Our ap->a_tdvp
	 * is expected to be locked on return.
	 */

	if ((tdvp = union_lock_upper(dun, td)) == NULLVP)
		return (EROFS);

	vn_unlock(ap->a_tdvp);	/* unlock calling node */
	error = VOP_LINK(tdvp, vp, cnp); /* call link on upper */

	/*
	 * We have to unlock tdvp prior to relocking our calling node in
	 * order to avoid a deadlock.
	 */
	union_unlock_upper(tdvp, td);
	vn_lock(ap->a_tdvp, LK_EXCLUSIVE | LK_RETRY);
	return (error);
}
/*
 *	union_link:
 *
 *	tdvp will be locked on entry, vp will not be locked on entry.
 *	tdvp should remain locked on return and vp should remain unlocked
 *	on return.
 *
 * union_link(struct vnode *a_tdvp, struct vnode *a_vp,
 *	      struct componentname *a_cnp)
 */
tdvp will be locked on entry, vp will not be locked on entry.
tdvp should remain locked on return and vp should remain unlocked
on return.


tdvp will be locked on entry vp will not be locked on entry . tdvp should remain locked on return and vp should remain unlocked on return .
static int
union_link(struct vop_old_link_args *ap)
{
	struct componentname *cnp = ap->a_cnp;
	struct thread *td = cnp->cn_td;
	struct union_node *dun = VTOUNION(ap->a_tdvp);
	struct vnode *vp;
	struct vnode *tdvp;
	int error = 0;
	if (ap->a_tdvp->v_ops != ap->a_vp->v_ops) {
		vp = ap->a_vp;
	} else {
		struct union_node *tun = VTOUNION(ap->a_vp);
		if (tun->un_uppervp == NULLVP) {
			vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY);
#if 0
			if (dun->un_uppervp == tun->un_dirvp) {
				if (dun->un_flags & UN_ULOCK) {
					dun->un_flags &= ~UN_ULOCK;
					vn_unlock(dun->un_uppervp);
				}
			}
#endif
			error = union_copyup(tun, 1, cnp->cn_cred, td);
#if 0
			if (dun->un_uppervp == tun->un_dirvp) {
				vn_lock(dun->un_uppervp, 
					LK_EXCLUSIVE | LK_RETRY);
				dun->un_flags |= UN_ULOCK;
			}
#endif
			vn_unlock(ap->a_vp);
		}
		vp = tun->un_uppervp;
	}
	if (error)
		return (error);
	if ((tdvp = union_lock_upper(dun, td)) == NULLVP)
		return (EROFS);
	vn_unlock(ap->a_tdvp);	
	error = VOP_LINK(tdvp, vp, cnp); 
	union_unlock_upper(tdvp, td);
	vn_lock(ap->a_tdvp, LK_EXCLUSIVE | LK_RETRY);
	return (error);
}
static int union_link ( struct vop_old_link_args * ap ) { struct componentname * cnp = ap -> a_cnp ; struct thread * td = cnp -> cn_td ; struct union_node * dun = VTOUNION ( ap -> a_tdvp ) ; struct vnode * vp ; struct vnode * tdvp ; int error = 0 ; if ( ap -> a_tdvp -> v_ops != ap -> a_vp -> v_ops ) { vp = ap -> a_vp ; } else { struct union_node * tun = VTOUNION ( ap -> a_vp ) ; if ( tun -> un_uppervp == NULLVP ) { vn_lock ( ap -> a_vp , LK_EXCLUSIVE | LK_RETRY ) ; #if 0 if ( dun -> un_uppervp == tun -> un_dirvp ) { if ( dun -> un_flags & UN_ULOCK ) { dun -> un_flags &= ~ UN_ULOCK ; vn_unlock ( dun -> un_uppervp ) ; } } #endif error = union_copyup ( tun , 1 , cnp -> cn_cred , td ) ; #if 0 if ( dun -> un_uppervp == tun -> un_dirvp ) { vn_lock ( dun -> un_uppervp , LK_EXCLUSIVE | LK_RETRY ) ; dun -> un_flags |= UN_ULOCK ; } #endif vn_unlock ( ap -> a_vp ) ; } vp = tun -> un_uppervp ; } if ( error ) return ( error ) ; if ( ( tdvp = union_lock_upper ( dun , td ) ) == NULLVP ) return ( EROFS ) ; vn_unlock ( ap -> a_tdvp ) ; error = VOP_LINK ( tdvp , vp , cnp ) ; union_unlock_upper ( tdvp , td ) ; vn_lock ( ap -> a_tdvp , LK_EXCLUSIVE | LK_RETRY ) ; return ( error ) ; }
union_link:
      tdvp will be locked on entry, vp will not be locked on entry.
union_link : tdvp will be locked on entry vp will not be locked on entry .
/*
	 * Make sure upper is locked, then unlock the union directory we were 
	 * called with to avoid a deadlock while we are calling VOP_LINK on 
	 * the upper (with tdvp locked and vp not locked).  Our ap->a_tdvp
	 * is expected to be locked on return.
	 */ /* unlock calling node */ /* call link on upper */ /*
	 * We have to unlock tdvp prior to relocking our calling node in
	 * order to avoid a deadlock.
	 */