xref: /freebsd/sys/ufs/ffs/ffs_suspend.c (revision df48361e7792f9a9e6371f95c1228d4af2808d2a)
11848286aSEdward Tomasz Napierala /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3fe267a55SPedro F. Giffuni  *
41848286aSEdward Tomasz Napierala  * Copyright (c) 2012 The FreeBSD Foundation
51848286aSEdward Tomasz Napierala  *
61848286aSEdward Tomasz Napierala  * This software was developed by Edward Tomasz Napierala under sponsorship
71848286aSEdward Tomasz Napierala  * from the FreeBSD Foundation.
81848286aSEdward Tomasz Napierala  *
91848286aSEdward Tomasz Napierala  * Redistribution and use in source and binary forms, with or without
101848286aSEdward Tomasz Napierala  * modification, are permitted provided that the following conditions
111848286aSEdward Tomasz Napierala  * are met:
121848286aSEdward Tomasz Napierala  * 1. Redistributions of source code must retain the above copyright
131848286aSEdward Tomasz Napierala  *    notice, this list of conditions and the following disclaimer.
141848286aSEdward Tomasz Napierala  * 2. Redistributions in binary form must reproduce the above copyright
151848286aSEdward Tomasz Napierala  *    notice, this list of conditions and the following disclaimer in the
161848286aSEdward Tomasz Napierala  *    documentation and/or other materials provided with the distribution.
171848286aSEdward Tomasz Napierala  *
181848286aSEdward Tomasz Napierala  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
191848286aSEdward Tomasz Napierala  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
201848286aSEdward Tomasz Napierala  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
211848286aSEdward Tomasz Napierala  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
221848286aSEdward Tomasz Napierala  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
231848286aSEdward Tomasz Napierala  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
241848286aSEdward Tomasz Napierala  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
251848286aSEdward Tomasz Napierala  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
261848286aSEdward Tomasz Napierala  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
271848286aSEdward Tomasz Napierala  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
281848286aSEdward Tomasz Napierala  * SUCH DAMAGE.
291848286aSEdward Tomasz Napierala  */
301848286aSEdward Tomasz Napierala 
311848286aSEdward Tomasz Napierala #include <sys/cdefs.h>
321848286aSEdward Tomasz Napierala #include <sys/systm.h>
338c7f0b92SGleb Smirnoff #include <sys/buf.h>
341848286aSEdward Tomasz Napierala #include <sys/conf.h>
35701b3696SKonstantin Belousov #include <sys/filedesc.h>
3668912701SKonstantin Belousov #include <sys/ioccom.h>
371848286aSEdward Tomasz Napierala #include <sys/jail.h>
3868912701SKonstantin Belousov #include <sys/mount.h>
391848286aSEdward Tomasz Napierala #include <sys/sx.h>
4068912701SKonstantin Belousov #include <sys/vnode.h>
411848286aSEdward Tomasz Napierala 
421848286aSEdward Tomasz Napierala #include <security/mac/mac_framework.h>
431848286aSEdward Tomasz Napierala 
441848286aSEdward Tomasz Napierala #include <ufs/ufs/extattr.h>
451848286aSEdward Tomasz Napierala #include <ufs/ufs/quota.h>
461848286aSEdward Tomasz Napierala #include <ufs/ufs/ufsmount.h>
471848286aSEdward Tomasz Napierala #include <ufs/ufs/inode.h>
481848286aSEdward Tomasz Napierala 
491848286aSEdward Tomasz Napierala #include <ufs/ffs/fs.h>
501848286aSEdward Tomasz Napierala #include <ufs/ffs/ffs_extern.h>
511848286aSEdward Tomasz Napierala 
521848286aSEdward Tomasz Napierala static d_open_t ffs_susp_open;
531848286aSEdward Tomasz Napierala static d_write_t ffs_susp_rdwr;
541848286aSEdward Tomasz Napierala static d_ioctl_t ffs_susp_ioctl;
551848286aSEdward Tomasz Napierala 
561848286aSEdward Tomasz Napierala static struct cdevsw ffs_susp_cdevsw = {
571848286aSEdward Tomasz Napierala 	.d_version =	D_VERSION,
581848286aSEdward Tomasz Napierala 	.d_open =	ffs_susp_open,
591848286aSEdward Tomasz Napierala 	.d_read =	ffs_susp_rdwr,
601848286aSEdward Tomasz Napierala 	.d_write =	ffs_susp_rdwr,
611848286aSEdward Tomasz Napierala 	.d_ioctl =	ffs_susp_ioctl,
621848286aSEdward Tomasz Napierala 	.d_name =	"ffs_susp",
631848286aSEdward Tomasz Napierala };
641848286aSEdward Tomasz Napierala 
651848286aSEdward Tomasz Napierala static struct cdev *ffs_susp_dev;
661848286aSEdward Tomasz Napierala static struct sx ffs_susp_lock;
671848286aSEdward Tomasz Napierala 
681848286aSEdward Tomasz Napierala static int
ffs_susp_suspended(struct mount * mp)691848286aSEdward Tomasz Napierala ffs_susp_suspended(struct mount *mp)
701848286aSEdward Tomasz Napierala {
711848286aSEdward Tomasz Napierala 	struct ufsmount *ump;
721848286aSEdward Tomasz Napierala 
731848286aSEdward Tomasz Napierala 	sx_assert(&ffs_susp_lock, SA_LOCKED);
741848286aSEdward Tomasz Napierala 
751848286aSEdward Tomasz Napierala 	ump = VFSTOUFS(mp);
76ab0bcb60SKirk McKusick 	if ((ump->um_flags & UM_WRITESUSPENDED) != 0)
771848286aSEdward Tomasz Napierala 		return (1);
781848286aSEdward Tomasz Napierala 	return (0);
791848286aSEdward Tomasz Napierala }
801848286aSEdward Tomasz Napierala 
811848286aSEdward Tomasz Napierala static int
ffs_susp_open(struct cdev * dev __unused,int flags __unused,int fmt __unused,struct thread * td __unused)821848286aSEdward Tomasz Napierala ffs_susp_open(struct cdev *dev __unused, int flags __unused,
831848286aSEdward Tomasz Napierala     int fmt __unused, struct thread *td __unused)
841848286aSEdward Tomasz Napierala {
851848286aSEdward Tomasz Napierala 
861848286aSEdward Tomasz Napierala 	return (0);
871848286aSEdward Tomasz Napierala }
881848286aSEdward Tomasz Napierala 
891848286aSEdward Tomasz Napierala static int
ffs_susp_rdwr(struct cdev * dev,struct uio * uio,int ioflag)901848286aSEdward Tomasz Napierala ffs_susp_rdwr(struct cdev *dev, struct uio *uio, int ioflag)
911848286aSEdward Tomasz Napierala {
921848286aSEdward Tomasz Napierala 	int error, i;
931848286aSEdward Tomasz Napierala 	struct vnode *devvp;
941848286aSEdward Tomasz Napierala 	struct mount *mp;
951848286aSEdward Tomasz Napierala 	struct ufsmount *ump;
961848286aSEdward Tomasz Napierala 	struct buf *bp;
971848286aSEdward Tomasz Napierala 	void *base;
981848286aSEdward Tomasz Napierala 	size_t len;
991848286aSEdward Tomasz Napierala 	ssize_t cnt;
1001848286aSEdward Tomasz Napierala 	struct fs *fs;
1011848286aSEdward Tomasz Napierala 
1021848286aSEdward Tomasz Napierala 	sx_slock(&ffs_susp_lock);
1031848286aSEdward Tomasz Napierala 
1041848286aSEdward Tomasz Napierala 	error = devfs_get_cdevpriv((void **)&mp);
1051848286aSEdward Tomasz Napierala 	if (error != 0) {
1061848286aSEdward Tomasz Napierala 		sx_sunlock(&ffs_susp_lock);
1071848286aSEdward Tomasz Napierala 		return (ENXIO);
1081848286aSEdward Tomasz Napierala 	}
1091848286aSEdward Tomasz Napierala 
1101848286aSEdward Tomasz Napierala 	ump = VFSTOUFS(mp);
1111848286aSEdward Tomasz Napierala 	devvp = ump->um_devvp;
1121848286aSEdward Tomasz Napierala 	fs = ump->um_fs;
1131848286aSEdward Tomasz Napierala 
1141848286aSEdward Tomasz Napierala 	if (ffs_susp_suspended(mp) == 0) {
1151848286aSEdward Tomasz Napierala 		sx_sunlock(&ffs_susp_lock);
1161848286aSEdward Tomasz Napierala 		return (ENXIO);
1171848286aSEdward Tomasz Napierala 	}
1181848286aSEdward Tomasz Napierala 
1191848286aSEdward Tomasz Napierala 	KASSERT(uio->uio_rw == UIO_READ || uio->uio_rw == UIO_WRITE,
1201848286aSEdward Tomasz Napierala 	    ("neither UIO_READ or UIO_WRITE"));
1211848286aSEdward Tomasz Napierala 	KASSERT(uio->uio_segflg == UIO_USERSPACE,
1221848286aSEdward Tomasz Napierala 	    ("uio->uio_segflg != UIO_USERSPACE"));
1231848286aSEdward Tomasz Napierala 
1241848286aSEdward Tomasz Napierala 	cnt = uio->uio_resid;
1251848286aSEdward Tomasz Napierala 
1261848286aSEdward Tomasz Napierala 	for (i = 0; i < uio->uio_iovcnt; i++) {
1271848286aSEdward Tomasz Napierala 		while (uio->uio_iov[i].iov_len) {
1281848286aSEdward Tomasz Napierala 			base = uio->uio_iov[i].iov_base;
1291848286aSEdward Tomasz Napierala 			len = uio->uio_iov[i].iov_len;
1301848286aSEdward Tomasz Napierala 			if (len > fs->fs_bsize)
1311848286aSEdward Tomasz Napierala 				len = fs->fs_bsize;
1321848286aSEdward Tomasz Napierala 			if (fragoff(fs, uio->uio_offset) != 0 ||
1331848286aSEdward Tomasz Napierala 			    fragoff(fs, len) != 0) {
1341848286aSEdward Tomasz Napierala 				error = EINVAL;
1351848286aSEdward Tomasz Napierala 				goto out;
1361848286aSEdward Tomasz Napierala 			}
1371848286aSEdward Tomasz Napierala 			error = bread(devvp, btodb(uio->uio_offset), len,
1381848286aSEdward Tomasz Napierala 			    NOCRED, &bp);
1391848286aSEdward Tomasz Napierala 			if (error != 0)
1401848286aSEdward Tomasz Napierala 				goto out;
141473c90acSJohn Baldwin 			switch (uio->uio_rw) {
142473c90acSJohn Baldwin 			case UIO_WRITE:
1431848286aSEdward Tomasz Napierala 				error = copyin(base, bp->b_data, len);
1441848286aSEdward Tomasz Napierala 				if (error != 0) {
1451848286aSEdward Tomasz Napierala 					bp->b_flags |= B_INVAL | B_NOCACHE;
1461848286aSEdward Tomasz Napierala 					brelse(bp);
1471848286aSEdward Tomasz Napierala 					goto out;
1481848286aSEdward Tomasz Napierala 				}
1491848286aSEdward Tomasz Napierala 				error = bwrite(bp);
1501848286aSEdward Tomasz Napierala 				if (error != 0)
1511848286aSEdward Tomasz Napierala 					goto out;
152473c90acSJohn Baldwin 				break;
153473c90acSJohn Baldwin 			case UIO_READ:
1541848286aSEdward Tomasz Napierala 				error = copyout(bp->b_data, base, len);
1551848286aSEdward Tomasz Napierala 				brelse(bp);
1561848286aSEdward Tomasz Napierala 				if (error != 0)
1571848286aSEdward Tomasz Napierala 					goto out;
158473c90acSJohn Baldwin 				break;
1591848286aSEdward Tomasz Napierala 			}
1601848286aSEdward Tomasz Napierala 			uio->uio_iov[i].iov_base =
1611848286aSEdward Tomasz Napierala 			    (char *)uio->uio_iov[i].iov_base + len;
1621848286aSEdward Tomasz Napierala 			uio->uio_iov[i].iov_len -= len;
1631848286aSEdward Tomasz Napierala 			uio->uio_resid -= len;
1641848286aSEdward Tomasz Napierala 			uio->uio_offset += len;
1651848286aSEdward Tomasz Napierala 		}
1661848286aSEdward Tomasz Napierala 	}
1671848286aSEdward Tomasz Napierala 
1681848286aSEdward Tomasz Napierala out:
1691848286aSEdward Tomasz Napierala 	sx_sunlock(&ffs_susp_lock);
1701848286aSEdward Tomasz Napierala 
1711848286aSEdward Tomasz Napierala 	if (uio->uio_resid < cnt)
1721848286aSEdward Tomasz Napierala 		return (0);
1731848286aSEdward Tomasz Napierala 
1741848286aSEdward Tomasz Napierala 	return (error);
1751848286aSEdward Tomasz Napierala }
1761848286aSEdward Tomasz Napierala 
1771848286aSEdward Tomasz Napierala static int
ffs_susp_suspend(struct mount * mp)1781848286aSEdward Tomasz Napierala ffs_susp_suspend(struct mount *mp)
1791848286aSEdward Tomasz Napierala {
1801848286aSEdward Tomasz Napierala 	struct ufsmount *ump;
1811848286aSEdward Tomasz Napierala 	int error;
1821848286aSEdward Tomasz Napierala 
1831848286aSEdward Tomasz Napierala 	sx_assert(&ffs_susp_lock, SA_XLOCKED);
1841848286aSEdward Tomasz Napierala 
1851848286aSEdward Tomasz Napierala 	if (!ffs_own_mount(mp))
1861848286aSEdward Tomasz Napierala 		return (EINVAL);
1871848286aSEdward Tomasz Napierala 	if (ffs_susp_suspended(mp))
1881848286aSEdward Tomasz Napierala 		return (EBUSY);
1891848286aSEdward Tomasz Napierala 
1901848286aSEdward Tomasz Napierala 	ump = VFSTOUFS(mp);
1911848286aSEdward Tomasz Napierala 
1921848286aSEdward Tomasz Napierala 	/*
1931848286aSEdward Tomasz Napierala 	 * Make sure the calling thread is permitted to access the mounted
1941848286aSEdward Tomasz Napierala 	 * device.  The permissions can change after we unlock the vnode;
1951848286aSEdward Tomasz Napierala 	 * it's harmless.
1961848286aSEdward Tomasz Napierala 	 */
19769b3fdfaSChuck Silvers 	vn_lock(ump->um_odevvp, LK_EXCLUSIVE | LK_RETRY);
19869b3fdfaSChuck Silvers 	error = VOP_ACCESS(ump->um_odevvp, VREAD | VWRITE,
1991848286aSEdward Tomasz Napierala 	    curthread->td_ucred, curthread);
20069b3fdfaSChuck Silvers 	VOP_UNLOCK(ump->um_odevvp);
2011848286aSEdward Tomasz Napierala 	if (error != 0)
2021848286aSEdward Tomasz Napierala 		return (error);
2031848286aSEdward Tomasz Napierala #ifdef MAC
2041848286aSEdward Tomasz Napierala 	if (mac_mount_check_stat(curthread->td_ucred, mp) != 0)
2051848286aSEdward Tomasz Napierala 		return (EPERM);
2061848286aSEdward Tomasz Napierala #endif
2071848286aSEdward Tomasz Napierala 
208cc3d8c35SKonstantin Belousov 	if ((error = vfs_write_suspend(mp, VS_SKIP_UNMOUNT)) != 0)
2091848286aSEdward Tomasz Napierala 		return (error);
2101848286aSEdward Tomasz Napierala 
21115430057SKirk McKusick 	UFS_LOCK(ump);
212ab0bcb60SKirk McKusick 	ump->um_flags |= UM_WRITESUSPENDED;
21315430057SKirk McKusick 	UFS_UNLOCK(ump);
2141848286aSEdward Tomasz Napierala 
2151848286aSEdward Tomasz Napierala 	return (0);
2161848286aSEdward Tomasz Napierala }
2171848286aSEdward Tomasz Napierala 
2181848286aSEdward Tomasz Napierala static void
ffs_susp_unsuspend(struct mount * mp)21961b285acSKonstantin Belousov ffs_susp_unsuspend(struct mount *mp)
22061b285acSKonstantin Belousov {
22161b285acSKonstantin Belousov 	struct ufsmount *ump;
22261b285acSKonstantin Belousov 
22361b285acSKonstantin Belousov 	sx_assert(&ffs_susp_lock, SA_XLOCKED);
22461b285acSKonstantin Belousov 
22561b285acSKonstantin Belousov 	/*
22661b285acSKonstantin Belousov 	 * XXX: The status is kept per-process; the vfs_write_resume() routine
22761b285acSKonstantin Belousov 	 * 	asserts that the resuming thread is the same one that called
22861b285acSKonstantin Belousov 	 * 	vfs_write_suspend().  The cdevpriv data, however, is attached
22961b285acSKonstantin Belousov 	 * 	to the file descriptor, e.g. is inherited during fork.  Thus,
23061b285acSKonstantin Belousov 	 * 	it's possible that the resuming process will be different from
23161b285acSKonstantin Belousov 	 * 	the one that started the suspension.
23261b285acSKonstantin Belousov 	 *
23361b285acSKonstantin Belousov 	 * 	Work around by fooling the check in vfs_write_resume().
23461b285acSKonstantin Belousov 	 */
23561b285acSKonstantin Belousov 	mp->mnt_susp_owner = curthread;
23661b285acSKonstantin Belousov 
23761b285acSKonstantin Belousov 	vfs_write_resume(mp, 0);
23861b285acSKonstantin Belousov 	ump = VFSTOUFS(mp);
23961b285acSKonstantin Belousov 	UFS_LOCK(ump);
24061b285acSKonstantin Belousov 	ump->um_flags &= ~UM_WRITESUSPENDED;
24161b285acSKonstantin Belousov 	UFS_UNLOCK(ump);
24261b285acSKonstantin Belousov 	vfs_unbusy(mp);
24361b285acSKonstantin Belousov }
24461b285acSKonstantin Belousov 
24561b285acSKonstantin Belousov static void
ffs_susp_dtor(void * data)2461848286aSEdward Tomasz Napierala ffs_susp_dtor(void *data)
2471848286aSEdward Tomasz Napierala {
2481848286aSEdward Tomasz Napierala 	struct mount *mp;
2491848286aSEdward Tomasz Napierala 	int error;
2501848286aSEdward Tomasz Napierala 
2511848286aSEdward Tomasz Napierala 	sx_xlock(&ffs_susp_lock);
2521848286aSEdward Tomasz Napierala 
2531848286aSEdward Tomasz Napierala 	mp = (struct mount *)data;
2541848286aSEdward Tomasz Napierala 
2551848286aSEdward Tomasz Napierala 	if (ffs_susp_suspended(mp) == 0) {
2561848286aSEdward Tomasz Napierala 		sx_xunlock(&ffs_susp_lock);
2571848286aSEdward Tomasz Napierala 		return;
2581848286aSEdward Tomasz Napierala 	}
2591848286aSEdward Tomasz Napierala 
2601848286aSEdward Tomasz Napierala 	KASSERT((mp->mnt_kern_flag & MNTK_SUSPEND) != 0,
2611848286aSEdward Tomasz Napierala 	    ("MNTK_SUSPEND not set"));
2621848286aSEdward Tomasz Napierala 
263440320b6SRobert Wing 	error = ffs_reload(mp, FFSR_FORCE | FFSR_UNSUSPEND);
2641848286aSEdward Tomasz Napierala 	if (error != 0)
265*df48361eSKirk McKusick 		panic("failed to unsuspend writes on %s",
266*df48361eSKirk McKusick 		    VFSTOUFS(mp)->um_fs->fs_fsmnt);
2671848286aSEdward Tomasz Napierala 
26861b285acSKonstantin Belousov 	ffs_susp_unsuspend(mp);
2691848286aSEdward Tomasz Napierala 	sx_xunlock(&ffs_susp_lock);
2701848286aSEdward Tomasz Napierala }
2711848286aSEdward Tomasz Napierala 
2721848286aSEdward Tomasz Napierala static int
ffs_susp_ioctl(struct cdev * dev,u_long cmd,caddr_t addr,int flags,struct thread * td)2731848286aSEdward Tomasz Napierala ffs_susp_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
2741848286aSEdward Tomasz Napierala     struct thread *td)
2751848286aSEdward Tomasz Napierala {
2761848286aSEdward Tomasz Napierala 	struct mount *mp;
2771848286aSEdward Tomasz Napierala 	fsid_t *fsidp;
2781848286aSEdward Tomasz Napierala 	int error;
2791848286aSEdward Tomasz Napierala 
2801848286aSEdward Tomasz Napierala 	/*
2811848286aSEdward Tomasz Napierala 	 * No suspend inside the jail.  Allowing it would require making
2821848286aSEdward Tomasz Napierala 	 * sure that e.g. the devfs ruleset for that jail permits access
2831848286aSEdward Tomasz Napierala 	 * to the devvp.
2841848286aSEdward Tomasz Napierala 	 */
2851848286aSEdward Tomasz Napierala 	if (jailed(td->td_ucred))
2861848286aSEdward Tomasz Napierala 		return (EPERM);
2871848286aSEdward Tomasz Napierala 
2881848286aSEdward Tomasz Napierala 	sx_xlock(&ffs_susp_lock);
2891848286aSEdward Tomasz Napierala 
2901848286aSEdward Tomasz Napierala 	switch (cmd) {
2911848286aSEdward Tomasz Napierala 	case UFSSUSPEND:
2921848286aSEdward Tomasz Napierala 		fsidp = (fsid_t *)addr;
2931848286aSEdward Tomasz Napierala 		mp = vfs_getvfs(fsidp);
2941848286aSEdward Tomasz Napierala 		if (mp == NULL) {
2951848286aSEdward Tomasz Napierala 			error = ENOENT;
2961848286aSEdward Tomasz Napierala 			break;
2971848286aSEdward Tomasz Napierala 		}
2981848286aSEdward Tomasz Napierala 		error = vfs_busy(mp, 0);
2991848286aSEdward Tomasz Napierala 		vfs_rel(mp);
3001848286aSEdward Tomasz Napierala 		if (error != 0)
3011848286aSEdward Tomasz Napierala 			break;
302701b3696SKonstantin Belousov 
303701b3696SKonstantin Belousov 		/*
304701b3696SKonstantin Belousov 		 * Require single-thread curproc so that the check is not racey.
305701b3696SKonstantin Belousov 		 * XXXKIB: might consider to singlethread curproc instead.
306701b3696SKonstantin Belousov 		 */
307701b3696SKonstantin Belousov 		error = curproc->p_numthreads > 1 ? EDEADLK :
308701b3696SKonstantin Belousov 		    descrip_check_write_mp(curproc->p_fd, mp);
309a5ff8664SKonstantin Belousov 		if (error != 0) {
310a5ff8664SKonstantin Belousov 			vfs_unbusy(mp);
311701b3696SKonstantin Belousov 			break;
312a5ff8664SKonstantin Belousov 		}
313701b3696SKonstantin Belousov 
3141848286aSEdward Tomasz Napierala 		error = ffs_susp_suspend(mp);
3151848286aSEdward Tomasz Napierala 		if (error != 0) {
3161848286aSEdward Tomasz Napierala 			vfs_unbusy(mp);
3171848286aSEdward Tomasz Napierala 			break;
3181848286aSEdward Tomasz Napierala 		}
3191848286aSEdward Tomasz Napierala 		error = devfs_set_cdevpriv(mp, ffs_susp_dtor);
32061b285acSKonstantin Belousov 		if (error != 0)
32161b285acSKonstantin Belousov 			ffs_susp_unsuspend(mp);
3221848286aSEdward Tomasz Napierala 		break;
3231848286aSEdward Tomasz Napierala 	case UFSRESUME:
3241848286aSEdward Tomasz Napierala 		error = devfs_get_cdevpriv((void **)&mp);
3251848286aSEdward Tomasz Napierala 		if (error != 0)
3261848286aSEdward Tomasz Napierala 			break;
3271848286aSEdward Tomasz Napierala 		/*
3281848286aSEdward Tomasz Napierala 		 * This calls ffs_susp_dtor, which in turn unsuspends the fs.
3291848286aSEdward Tomasz Napierala 		 * The dtor expects to be called without lock held, because
3301848286aSEdward Tomasz Napierala 		 * sometimes it's called from here, and sometimes due to the
3311848286aSEdward Tomasz Napierala 		 * file being closed or process exiting.
3321848286aSEdward Tomasz Napierala 		 */
3331848286aSEdward Tomasz Napierala 		sx_xunlock(&ffs_susp_lock);
3341848286aSEdward Tomasz Napierala 		devfs_clear_cdevpriv();
3351848286aSEdward Tomasz Napierala 		return (0);
3361848286aSEdward Tomasz Napierala 	default:
3371848286aSEdward Tomasz Napierala 		error = ENXIO;
3381848286aSEdward Tomasz Napierala 		break;
3391848286aSEdward Tomasz Napierala 	}
3401848286aSEdward Tomasz Napierala 
3411848286aSEdward Tomasz Napierala 	sx_xunlock(&ffs_susp_lock);
3421848286aSEdward Tomasz Napierala 
3431848286aSEdward Tomasz Napierala 	return (error);
3441848286aSEdward Tomasz Napierala }
3451848286aSEdward Tomasz Napierala 
3461848286aSEdward Tomasz Napierala void
ffs_susp_initialize(void)3471848286aSEdward Tomasz Napierala ffs_susp_initialize(void)
3481848286aSEdward Tomasz Napierala {
3491848286aSEdward Tomasz Napierala 
3501848286aSEdward Tomasz Napierala 	sx_init(&ffs_susp_lock, "ffs_susp");
3511848286aSEdward Tomasz Napierala 	ffs_susp_dev = make_dev(&ffs_susp_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
3521848286aSEdward Tomasz Napierala 	    "ufssuspend");
3531848286aSEdward Tomasz Napierala }
3541848286aSEdward Tomasz Napierala 
3551848286aSEdward Tomasz Napierala void
ffs_susp_uninitialize(void)3561848286aSEdward Tomasz Napierala ffs_susp_uninitialize(void)
3571848286aSEdward Tomasz Napierala {
3581848286aSEdward Tomasz Napierala 
3591848286aSEdward Tomasz Napierala 	destroy_dev(ffs_susp_dev);
3601848286aSEdward Tomasz Napierala 	sx_destroy(&ffs_susp_lock);
3611848286aSEdward Tomasz Napierala }
362