Index: head/sys/ufs/ffs/ffs_suspend.c =================================================================== --- head/sys/ufs/ffs/ffs_suspend.c (revision 336360) +++ head/sys/ufs/ffs/ffs_suspend.c (revision 336361) @@ -1,339 +1,343 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static d_open_t ffs_susp_open; static d_write_t ffs_susp_rdwr; static d_ioctl_t ffs_susp_ioctl; static struct cdevsw ffs_susp_cdevsw = { .d_version = D_VERSION, .d_open = ffs_susp_open, .d_read = ffs_susp_rdwr, .d_write = ffs_susp_rdwr, .d_ioctl = ffs_susp_ioctl, .d_name = "ffs_susp", }; static struct cdev *ffs_susp_dev; static struct sx ffs_susp_lock; static int ffs_susp_suspended(struct mount *mp) { struct ufsmount *ump; sx_assert(&ffs_susp_lock, SA_LOCKED); ump = VFSTOUFS(mp); if ((ump->um_flags & UM_WRITESUSPENDED) != 0) return (1); return (0); } static int ffs_susp_open(struct cdev *dev __unused, int flags __unused, int fmt __unused, struct thread *td __unused) { return (0); } static int ffs_susp_rdwr(struct cdev *dev, struct uio *uio, int ioflag) { int error, i; struct vnode *devvp; struct mount *mp; struct ufsmount *ump; struct buf *bp; void *base; size_t len; ssize_t cnt; struct fs *fs; sx_slock(&ffs_susp_lock); error = devfs_get_cdevpriv((void **)&mp); if (error != 0) { sx_sunlock(&ffs_susp_lock); return (ENXIO); } ump = VFSTOUFS(mp); devvp = ump->um_devvp; fs = ump->um_fs; if (ffs_susp_suspended(mp) == 0) { sx_sunlock(&ffs_susp_lock); return (ENXIO); } KASSERT(uio->uio_rw == UIO_READ || uio->uio_rw == UIO_WRITE, ("neither UIO_READ or UIO_WRITE")); KASSERT(uio->uio_segflg == UIO_USERSPACE, ("uio->uio_segflg != UIO_USERSPACE")); cnt = uio->uio_resid; for (i = 0; i < uio->uio_iovcnt; i++) { while (uio->uio_iov[i].iov_len) { base = uio->uio_iov[i].iov_base; len = uio->uio_iov[i].iov_len; if (len > fs->fs_bsize) len = fs->fs_bsize; if (fragoff(fs, uio->uio_offset) != 0 || fragoff(fs, len) != 0) { error = EINVAL; goto out; } error = bread(devvp, btodb(uio->uio_offset), len, NOCRED, &bp); if (error != 0) goto out; if (uio->uio_rw == UIO_WRITE) { error = copyin(base, bp->b_data, len); if (error != 0) { bp->b_flags |= B_INVAL | B_NOCACHE; brelse(bp); goto out; } error = bwrite(bp); if (error != 0) goto out; } else { error = copyout(bp->b_data, base, len); brelse(bp); if (error != 0) goto out; } uio->uio_iov[i].iov_base = (char *)uio->uio_iov[i].iov_base + len; uio->uio_iov[i].iov_len -= len; uio->uio_resid -= len; uio->uio_offset += len; } } out: sx_sunlock(&ffs_susp_lock); if (uio->uio_resid < cnt) return (0); return (error); } static int ffs_susp_suspend(struct mount *mp) { struct ufsmount *ump; int error; sx_assert(&ffs_susp_lock, SA_XLOCKED); if (!ffs_own_mount(mp)) return (EINVAL); if (ffs_susp_suspended(mp)) return (EBUSY); ump = VFSTOUFS(mp); /* * Make sure the calling thread is permitted to access the mounted * device. The permissions can change after we unlock the vnode; * it's harmless. */ vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY); error = VOP_ACCESS(ump->um_devvp, VREAD | VWRITE, curthread->td_ucred, curthread); VOP_UNLOCK(ump->um_devvp, 0); if (error != 0) return (error); #ifdef MAC if (mac_mount_check_stat(curthread->td_ucred, mp) != 0) return (EPERM); #endif if ((error = vfs_write_suspend(mp, VS_SKIP_UNMOUNT)) != 0) return (error); + UFS_LOCK(ump); ump->um_flags |= UM_WRITESUSPENDED; + UFS_UNLOCK(ump); return (0); } static void ffs_susp_dtor(void *data) { struct fs *fs; struct ufsmount *ump; struct mount *mp; int error; sx_xlock(&ffs_susp_lock); mp = (struct mount *)data; ump = VFSTOUFS(mp); fs = ump->um_fs; if (ffs_susp_suspended(mp) == 0) { sx_xunlock(&ffs_susp_lock); return; } KASSERT((mp->mnt_kern_flag & MNTK_SUSPEND) != 0, ("MNTK_SUSPEND not set")); error = ffs_reload(mp, curthread, FFSR_FORCE | FFSR_UNSUSPEND); if (error != 0) panic("failed to unsuspend writes on %s", fs->fs_fsmnt); /* * XXX: The status is kept per-process; the vfs_write_resume() routine * asserts that the resuming thread is the same one that called * vfs_write_suspend(). The cdevpriv data, however, is attached * to the file descriptor, e.g. is inherited during fork. Thus, * it's possible that the resuming process will be different from * the one that started the suspension. * * Work around by fooling the check in vfs_write_resume(). */ mp->mnt_susp_owner = curthread; vfs_write_resume(mp, 0); vfs_unbusy(mp); + UFS_LOCK(ump); ump->um_flags &= ~UM_WRITESUSPENDED; + UFS_UNLOCK(ump); sx_xunlock(&ffs_susp_lock); } static int ffs_susp_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) { struct mount *mp; fsid_t *fsidp; int error; /* * No suspend inside the jail. Allowing it would require making * sure that e.g. the devfs ruleset for that jail permits access * to the devvp. */ if (jailed(td->td_ucred)) return (EPERM); sx_xlock(&ffs_susp_lock); switch (cmd) { case UFSSUSPEND: fsidp = (fsid_t *)addr; mp = vfs_getvfs(fsidp); if (mp == NULL) { error = ENOENT; break; } error = vfs_busy(mp, 0); vfs_rel(mp); if (error != 0) break; error = ffs_susp_suspend(mp); if (error != 0) { vfs_unbusy(mp); break; } error = devfs_set_cdevpriv(mp, ffs_susp_dtor); KASSERT(error == 0, ("devfs_set_cdevpriv failed")); break; case UFSRESUME: error = devfs_get_cdevpriv((void **)&mp); if (error != 0) break; /* * This calls ffs_susp_dtor, which in turn unsuspends the fs. * The dtor expects to be called without lock held, because * sometimes it's called from here, and sometimes due to the * file being closed or process exiting. */ sx_xunlock(&ffs_susp_lock); devfs_clear_cdevpriv(); return (0); default: error = ENXIO; break; } sx_xunlock(&ffs_susp_lock); return (error); } void ffs_susp_initialize(void) { sx_init(&ffs_susp_lock, "ffs_susp"); ffs_susp_dev = make_dev(&ffs_susp_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "ufssuspend"); } void ffs_susp_uninitialize(void) { destroy_dev(ffs_susp_dev); sx_destroy(&ffs_susp_lock); } Index: head/sys/ufs/ufs/ufsmount.h =================================================================== --- head/sys/ufs/ufs/ufsmount.h (revision 336360) +++ head/sys/ufs/ufs/ufsmount.h (revision 336361) @@ -1,154 +1,167 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. 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, 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. 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. * * @(#)ufsmount.h 8.6 (Berkeley) 3/30/95 * $FreeBSD$ */ #ifndef _UFS_UFS_UFSMOUNT_H_ #define _UFS_UFS_UFSMOUNT_H_ /* * Arguments to mount UFS-based filesystems */ struct ufs_args { char *fspec; /* block special device to mount */ struct oexport_args export; /* network export information */ }; #ifdef _KERNEL #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_UFSMNT); #endif struct buf; struct inode; struct nameidata; struct taskqueue; struct timeval; struct ucred; struct uio; struct vnode; struct ufs_extattr_per_mount; struct jblocks; struct inodedep; TAILQ_HEAD(inodedeplst, inodedep); LIST_HEAD(bmsafemaphd, bmsafemap); -/* This structure describes the UFS specific mount structure data. */ +/* + * This structure describes the UFS specific mount structure data. + * The function operators are used to support different versions of + * UFS (UFS1, UFS2, etc). + * + * Lock reference: + * a - atomic operations + * c - set at allocation then constant until freed + * i - ufsmount interlock (UFS_LOCK / UFS_UNLOCK) + * q - associated quota file is locked + * r - ref to parent mount structure is held (vfs_busy / vfs_unbusy) + * u - managed by user process fsck_ufs + */ struct ufsmount { - struct mount *um_mountp; /* filesystem vfs structure */ - struct cdev *um_dev; /* device mounted */ - struct g_consumer *um_cp; - struct bufobj *um_bo; /* Buffer cache object */ - struct vnode *um_devvp; /* block device mounted vnode */ - u_long um_fstype; /* type of filesystem */ - struct fs *um_fs; /* pointer to superblock */ - struct ufs_extattr_per_mount um_extattr; /* extended attrs */ - u_long um_nindir; /* indirect ptrs per block */ - u_long um_bptrtodb; /* indir ptr to disk block */ - u_long um_seqinc; /* inc between seq blocks */ - struct mtx um_lock; /* Protects ufsmount & fs */ - pid_t um_fsckpid; /* PID permitted fsck sysctls */ - struct mount_softdeps *um_softdep; /* softdep mgmt structure */ - struct vnode *um_quotas[MAXQUOTAS]; /* pointer to quota files */ - struct ucred *um_cred[MAXQUOTAS]; /* quota file access cred */ - time_t um_btime[MAXQUOTAS]; /* block quota time limit */ - time_t um_itime[MAXQUOTAS]; /* inode quota time limit */ - char um_qflags[MAXQUOTAS]; /* quota specific flags */ - int64_t um_savedmaxfilesize; /* XXX - limit maxfilesize */ - u_int um_flags; /* filesystem flags */ - u_int um_trim_inflight; /* outstanding trim count */ - struct taskqueue *um_trim_tq; /* trim request queue */ + struct mount *um_mountp; /* (r) filesystem vfs struct */ + struct cdev *um_dev; /* (r) device mounted */ + struct g_consumer *um_cp; /* (r) GEOM access point */ + struct bufobj *um_bo; /* (r) Buffer cache object */ + struct vnode *um_devvp; /* (r) blk dev mounted vnode */ + u_long um_fstype; /* (c) type of filesystem */ + struct fs *um_fs; /* (r) pointer to superblock */ + struct ufs_extattr_per_mount um_extattr; /* (c) extended attrs */ + u_long um_nindir; /* (c) indirect ptrs per blk */ + u_long um_bptrtodb; /* (c) indir disk block ptr */ + u_long um_seqinc; /* (c) inc between seq blocks */ + struct mtx um_lock; /* (c) Protects ufsmount & fs */ + pid_t um_fsckpid; /* (u) PID can do fsck sysctl */ + struct mount_softdeps *um_softdep; /* (c) softdep mgmt structure */ + struct vnode *um_quotas[MAXQUOTAS]; /* (q) pointer to quota files */ + struct ucred *um_cred[MAXQUOTAS]; /* (q) quota file access cred */ + time_t um_btime[MAXQUOTAS]; /* (q) block quota time limit */ + time_t um_itime[MAXQUOTAS]; /* (q) inode quota time limit */ + char um_qflags[MAXQUOTAS]; /* (i) quota specific flags */ + int64_t um_savedmaxfilesize; /* (c) track maxfilesize */ + u_int um_flags; /* (i) filesystem flags */ + u_int um_trim_inflight; /* (a) outstanding trim count */ + struct taskqueue *um_trim_tq; /* (c) trim request queue */ + /* (c) - below function ptrs */ int (*um_balloc)(struct vnode *, off_t, int, struct ucred *, int, struct buf **); int (*um_blkatoff)(struct vnode *, off_t, char **, struct buf **); int (*um_truncate)(struct vnode *, off_t, int, struct ucred *); int (*um_update)(struct vnode *, int); int (*um_valloc)(struct vnode *, int, struct ucred *, struct vnode **); int (*um_vfree)(struct vnode *, ino_t, int); void (*um_ifree)(struct ufsmount *, struct inode *); int (*um_rdonly)(struct inode *); void (*um_snapgone)(struct inode *); }; /* * filesystem flags */ #define UM_CANDELETE 0x00000001 /* devvp supports TRIM */ #define UM_WRITESUSPENDED 0x00000002 /* suspension in progress */ /* * function prototypes */ #define UFS_BALLOC(aa, bb, cc, dd, ee, ff) VFSTOUFS((aa)->v_mount)->um_balloc(aa, bb, cc, dd, ee, ff) #define UFS_BLKATOFF(aa, bb, cc, dd) VFSTOUFS((aa)->v_mount)->um_blkatoff(aa, bb, cc, dd) #define UFS_TRUNCATE(aa, bb, cc, dd) VFSTOUFS((aa)->v_mount)->um_truncate(aa, bb, cc, dd) #define UFS_UPDATE(aa, bb) VFSTOUFS((aa)->v_mount)->um_update(aa, bb) #define UFS_VALLOC(aa, bb, cc, dd) VFSTOUFS((aa)->v_mount)->um_valloc(aa, bb, cc, dd) #define UFS_VFREE(aa, bb, cc) VFSTOUFS((aa)->v_mount)->um_vfree(aa, bb, cc) #define UFS_IFREE(aa, bb) ((aa)->um_ifree(aa, bb)) #define UFS_RDONLY(aa) (ITOUMP(aa)->um_rdonly(aa)) #define UFS_SNAPGONE(aa) (ITOUMP(aa)->um_snapgone(aa)) #define UFS_LOCK(aa) mtx_lock(&(aa)->um_lock) #define UFS_UNLOCK(aa) mtx_unlock(&(aa)->um_lock) #define UFS_MTX(aa) (&(aa)->um_lock) /* * Filesystem types */ #define UFS1 1 #define UFS2 2 /* * Flags describing the state of quotas. */ #define QTF_OPENING 0x01 /* Q_QUOTAON in progress */ #define QTF_CLOSING 0x02 /* Q_QUOTAOFF in progress */ #define QTF_64BIT 0x04 /* 64-bit quota file */ /* Convert mount ptr to ufsmount ptr. */ #define VFSTOUFS(mp) ((struct ufsmount *)((mp)->mnt_data)) #define UFSTOVFS(ump) (ump)->um_mountp /* * Macros to access filesystem parameters in the ufsmount structure. * Used by ufs_bmap. */ #define MNINDIR(ump) ((ump)->um_nindir) #define blkptrtodb(ump, b) ((b) << (ump)->um_bptrtodb) #define is_sequential(ump, a, b) ((b) == (a) + ump->um_seqinc) #endif /* _KERNEL */ #endif