FreeBSD/sys/kern/vfs_mount.c
/*
 * Do the actual filesystem unmount.
 */
Do the actual filesystem unmount.
Do the actual filesystem unmount .
int
dounmount(struct mount *mp, int flags, struct thread *td)
{
	struct vnode *coveredvp, *fsrootvp;
	int error;
	uint64_t async_flag;
	int mnt_gen_r;
	if ((coveredvp = mp->mnt_vnodecovered) != NULL) {
		mnt_gen_r = mp->mnt_gen;
		VI_LOCK(coveredvp);
		vholdl(coveredvp);
		vn_lock(coveredvp, LK_EXCLUSIVE | LK_INTERLOCK | LK_RETRY);
		if (coveredvp->v_mountedhere != mp ||
		    coveredvp->v_mountedhere->mnt_gen != mnt_gen_r) {
			VOP_UNLOCK(coveredvp, 0);
			vdrop(coveredvp);
			vfs_rel(mp);
			return (EBUSY);
		}
	}
	error = vfs_suser(mp, td);
	if (error != 0) {
		if (coveredvp != NULL) {
			VOP_UNLOCK(coveredvp, 0);
			vdrop(coveredvp);
		}
		vfs_rel(mp);
		return (error);
	}
	vn_start_write(NULL, &mp, V_WAIT | V_MNTREF);
	MNT_ILOCK(mp);
	if ((mp->mnt_kern_flag & MNTK_UNMOUNT) != 0 ||
	    !TAILQ_EMPTY(&mp->mnt_uppers)) {
		MNT_IUNLOCK(mp);
		if (coveredvp != NULL) {
			VOP_UNLOCK(coveredvp, 0);
			vdrop(coveredvp);
		}
		vn_finished_write(mp);
		return (EBUSY);
	}
	mp->mnt_kern_flag |= MNTK_UNMOUNT | MNTK_NOINSMNTQ;
	if (flags & MNT_NONBUSY) {
		MNT_IUNLOCK(mp);
		error = vfs_check_usecounts(mp);
		MNT_ILOCK(mp);
		if (error != 0) {
			mp->mnt_kern_flag &= ~(MNTK_UNMOUNT | MNTK_NOINSMNTQ);
			MNT_IUNLOCK(mp);
			if (coveredvp != NULL) {
				VOP_UNLOCK(coveredvp, 0);
				vdrop(coveredvp);
			}
			vn_finished_write(mp);
			return (error);
		}
	}
	if (flags & MNT_FORCE) {
		mp->mnt_kern_flag |= MNTK_UNMOUNTF;
		MNT_IUNLOCK(mp);
		VFS_PURGE(mp);
		MNT_ILOCK(mp);
	}
	error = 0;
	if (mp->mnt_lockref) {
		mp->mnt_kern_flag |= MNTK_DRAINING;
		error = msleep(&mp->mnt_lockref, MNT_MTX(mp), PVFS,
		    "mount drain", 0);
	}
	MNT_IUNLOCK(mp);
	KASSERT(mp->mnt_lockref == 0,
	    ("%s: invalid lock refcount in the drain path @ %s:%d",
	    __func__, __FILE__, __LINE__));
	KASSERT(error == 0,
	    ("%s: invalid return value for msleep in the drain path @ %s:%d",
	    __func__, __FILE__, __LINE__));
	if (mp->mnt_flag & MNT_EXPUBLIC)
		vfs_setpublicfs(NULL, NULL, NULL);
	if (coveredvp != NULL)
		vdrop(coveredvp);
	vfs_msync(mp, MNT_WAIT);
	MNT_ILOCK(mp);
	async_flag = mp->mnt_flag & MNT_ASYNC;
	mp->mnt_flag &= ~MNT_ASYNC;
	mp->mnt_kern_flag &= ~MNTK_ASYNC;
	MNT_IUNLOCK(mp);
	cache_purgevfs(mp);	
	vfs_deallocate_syncvnode(mp);
	if ((flags & MNT_FORCE) &&
	    VFS_ROOT(mp, LK_EXCLUSIVE, &fsrootvp) == 0) {
		if (mp->mnt_vnodecovered != NULL &&
		    (mp->mnt_flag & MNT_IGNORE) == 0)
			mountcheckdirs(fsrootvp, mp->mnt_vnodecovered);
		if (fsrootvp == rootvnode) {
			vrele(rootvnode);
			rootvnode = NULL;
		}
		vput(fsrootvp);
	}
	if ((mp->mnt_flag & MNT_RDONLY) != 0 || (flags & MNT_FORCE) != 0 ||
	    (error = VFS_SYNC(mp, MNT_WAIT)) == 0)
		error = VFS_UNMOUNT(mp, flags);
	vn_finished_write(mp);
	if (error && error != ENXIO) {
		if ((flags & MNT_FORCE) &&
		    VFS_ROOT(mp, LK_EXCLUSIVE, &fsrootvp) == 0) {
			if (mp->mnt_vnodecovered != NULL &&
			    (mp->mnt_flag & MNT_IGNORE) == 0)
				mountcheckdirs(mp->mnt_vnodecovered, fsrootvp);
			if (rootvnode == NULL) {
				rootvnode = fsrootvp;
				vref(rootvnode);
			}
			vput(fsrootvp);
		}
		MNT_ILOCK(mp);
		mp->mnt_kern_flag &= ~MNTK_NOINSMNTQ;
		if ((mp->mnt_flag & MNT_RDONLY) == 0) {
			MNT_IUNLOCK(mp);
			vfs_allocate_syncvnode(mp);
			MNT_ILOCK(mp);
		}
		mp->mnt_kern_flag &= ~(MNTK_UNMOUNT | MNTK_UNMOUNTF);
		mp->mnt_flag |= async_flag;
		if ((mp->mnt_flag & MNT_ASYNC) != 0 &&
		    (mp->mnt_kern_flag & MNTK_NOASYNC) == 0)
			mp->mnt_kern_flag |= MNTK_ASYNC;
		if (mp->mnt_kern_flag & MNTK_MWAIT) {
			mp->mnt_kern_flag &= ~MNTK_MWAIT;
			wakeup(mp);
		}
		MNT_IUNLOCK(mp);
		if (coveredvp)
			VOP_UNLOCK(coveredvp, 0);
		return (error);
	}
	mtx_lock(&mountlist_mtx);
	TAILQ_REMOVE(&mountlist, mp, mnt_list);
	mtx_unlock(&mountlist_mtx);
	EVENTHANDLER_INVOKE(vfs_unmounted, mp, td);
	if (coveredvp != NULL) {
		coveredvp->v_mountedhere = NULL;
		vput(coveredvp);
	}
	vfs_event_signal(NULL, VQ_UNMOUNT, 0);
	if (mp == rootdevmp)
		rootdevmp = NULL;
	vfs_mount_destroy(mp);
	return (0);
}
int dounmount ( struct mount * mp , int flags , struct thread * td ) { struct vnode * coveredvp , * fsrootvp ; int error ; uint64_t async_flag ; int mnt_gen_r ; if ( ( coveredvp = mp -> mnt_vnodecovered ) != NULL ) { mnt_gen_r = mp -> mnt_gen ; VI_LOCK ( coveredvp ) ; vholdl ( coveredvp ) ; vn_lock ( coveredvp , LK_EXCLUSIVE | LK_INTERLOCK | LK_RETRY ) ; if ( coveredvp -> v_mountedhere != mp || coveredvp -> v_mountedhere -> mnt_gen != mnt_gen_r ) { VOP_UNLOCK ( coveredvp , 0 ) ; vdrop ( coveredvp ) ; vfs_rel ( mp ) ; return ( EBUSY ) ; } } error = vfs_suser ( mp , td ) ; if ( error != 0 ) { if ( coveredvp != NULL ) { VOP_UNLOCK ( coveredvp , 0 ) ; vdrop ( coveredvp ) ; } vfs_rel ( mp ) ; return ( error ) ; } vn_start_write ( NULL , & mp , V_WAIT | V_MNTREF ) ; MNT_ILOCK ( mp ) ; if ( ( mp -> mnt_kern_flag & MNTK_UNMOUNT ) != 0 || ! TAILQ_EMPTY ( & mp -> mnt_uppers ) ) { MNT_IUNLOCK ( mp ) ; if ( coveredvp != NULL ) { VOP_UNLOCK ( coveredvp , 0 ) ; vdrop ( coveredvp ) ; } vn_finished_write ( mp ) ; return ( EBUSY ) ; } mp -> mnt_kern_flag |= MNTK_UNMOUNT | MNTK_NOINSMNTQ ; if ( flags & MNT_NONBUSY ) { MNT_IUNLOCK ( mp ) ; error = vfs_check_usecounts ( mp ) ; MNT_ILOCK ( mp ) ; if ( error != 0 ) { mp -> mnt_kern_flag &= ~ ( MNTK_UNMOUNT | MNTK_NOINSMNTQ ) ; MNT_IUNLOCK ( mp ) ; if ( coveredvp != NULL ) { VOP_UNLOCK ( coveredvp , 0 ) ; vdrop ( coveredvp ) ; } vn_finished_write ( mp ) ; return ( error ) ; } } if ( flags & MNT_FORCE ) { mp -> mnt_kern_flag |= MNTK_UNMOUNTF ; MNT_IUNLOCK ( mp ) ; VFS_PURGE ( mp ) ; MNT_ILOCK ( mp ) ; } error = 0 ; if ( mp -> mnt_lockref ) { mp -> mnt_kern_flag |= MNTK_DRAINING ; error = msleep ( & mp -> mnt_lockref , MNT_MTX ( mp ) , PVFS , " " , 0 ) ; } MNT_IUNLOCK ( mp ) ; KASSERT ( mp -> mnt_lockref == 0 , ( " " , __func__ , __FILE__ , __LINE__ ) ) ; KASSERT ( error == 0 , ( " " , __func__ , __FILE__ , __LINE__ ) ) ; if ( mp -> mnt_flag & MNT_EXPUBLIC ) vfs_setpublicfs ( NULL , NULL , NULL ) ; if ( coveredvp != NULL ) vdrop ( coveredvp ) ; vfs_msync ( mp , MNT_WAIT ) ; MNT_ILOCK ( mp ) ; async_flag = mp -> mnt_flag & MNT_ASYNC ; mp -> mnt_flag &= ~ MNT_ASYNC ; mp -> mnt_kern_flag &= ~ MNTK_ASYNC ; MNT_IUNLOCK ( mp ) ; cache_purgevfs ( mp ) ; vfs_deallocate_syncvnode ( mp ) ; if ( ( flags & MNT_FORCE ) && VFS_ROOT ( mp , LK_EXCLUSIVE , & fsrootvp ) == 0 ) { if ( mp -> mnt_vnodecovered != NULL && ( mp -> mnt_flag & MNT_IGNORE ) == 0 ) mountcheckdirs ( fsrootvp , mp -> mnt_vnodecovered ) ; if ( fsrootvp == rootvnode ) { vrele ( rootvnode ) ; rootvnode = NULL ; } vput ( fsrootvp ) ; } if ( ( mp -> mnt_flag & MNT_RDONLY ) != 0 || ( flags & MNT_FORCE ) != 0 || ( error = VFS_SYNC ( mp , MNT_WAIT ) ) == 0 ) error = VFS_UNMOUNT ( mp , flags ) ; vn_finished_write ( mp ) ; if ( error && error != ENXIO ) { if ( ( flags & MNT_FORCE ) && VFS_ROOT ( mp , LK_EXCLUSIVE , & fsrootvp ) == 0 ) { if ( mp -> mnt_vnodecovered != NULL && ( mp -> mnt_flag & MNT_IGNORE ) == 0 ) mountcheckdirs ( mp -> mnt_vnodecovered , fsrootvp ) ; if ( rootvnode == NULL ) { rootvnode = fsrootvp ; vref ( rootvnode ) ; } vput ( fsrootvp ) ; } MNT_ILOCK ( mp ) ; mp -> mnt_kern_flag &= ~ MNTK_NOINSMNTQ ; if ( ( mp -> mnt_flag & MNT_RDONLY ) == 0 ) { MNT_IUNLOCK ( mp ) ; vfs_allocate_syncvnode ( mp ) ; MNT_ILOCK ( mp ) ; } mp -> mnt_kern_flag &= ~ ( MNTK_UNMOUNT | MNTK_UNMOUNTF ) ; mp -> mnt_flag |= async_flag ; if ( ( mp -> mnt_flag & MNT_ASYNC ) != 0 && ( mp -> mnt_kern_flag & MNTK_NOASYNC ) == 0 ) mp -> mnt_kern_flag |= MNTK_ASYNC ; if ( mp -> mnt_kern_flag & MNTK_MWAIT ) { mp -> mnt_kern_flag &= ~ MNTK_MWAIT ; wakeup ( mp ) ; } MNT_IUNLOCK ( mp ) ; if ( coveredvp ) VOP_UNLOCK ( coveredvp , 0 ) ; return ( error ) ; } mtx_lock ( & mountlist_mtx ) ; TAILQ_REMOVE ( & mountlist , mp , mnt_list ) ; mtx_unlock ( & mountlist_mtx ) ; EVENTHANDLER_INVOKE ( vfs_unmounted , mp , td ) ; if ( coveredvp != NULL ) { coveredvp -> v_mountedhere = NULL ; vput ( coveredvp ) ; } vfs_event_signal ( NULL , VQ_UNMOUNT , 0 ) ; if ( mp == rootdevmp ) rootdevmp = NULL ; vfs_mount_destroy ( mp ) ; return ( 0 ) ; }
Do the actual filesystem unmount.
Do the actual filesystem unmount .
/*
		 * Check for mp being unmounted while waiting for the
		 * covered vnode lock.
		 */ /*
	 * Only privileged root, or (if MNT_USER is set) the user that did the
	 * original mount is permitted to unmount this filesystem.
	 */ /* Allow filesystems to detect that a forced unmount is in progress. */ /*
		 * Must be done after setting MNTK_UNMOUNTF and before
		 * waiting for mnt_lockref to become 0.
		 */ /*
	 * From now, we can claim that the use reference on the
	 * coveredvp is ours, and the ref can be released only by
	 * successfull unmount by us, or left for later unmount
	 * attempt.  The previously acquired hold reference is no
	 * longer needed to protect the vnode from reuse.
	 */ /* remove cache entries for this file sys */ /*
	 * For forced unmounts, move process cdir/rdir refs on the fs root
	 * vnode to the covered vnode.  For non-forced unmounts we want
	 * such references to cause an EBUSY error.
	 */ /*
	 * If we failed to flush the dirty blocks for this mount point,
	 * undo all the cdir/rdir and rootvnode changes we made above.
	 * Unless we failed to do so because the device is reporting that
	 * it doesn't exist anymore.
	 */