diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -4172,6 +4172,29 @@ vrele(olddp); } +int +descrip_check_write_mp(struct filedesc *fdp, struct mount *mp) +{ + struct file *fp; + struct vnode *vp; + int error, i; + + error = 0; + FILEDESC_SLOCK(fdp); + FILEDESC_FOREACH_FP(fdp, i, fp) { + if (fp->f_type != DTYPE_VNODE || + (atomic_load_int(&fp->f_flag) & FWRITE) == 0) + continue; + vp = fp->f_vnode; + if (vp->v_mount == mp) { + error = EDEADLK; + break; + } + } + FILEDESC_SUNLOCK(fdp); + return (error); +} + struct filedesc_to_leader * filedesc_to_leader_alloc(struct filedesc_to_leader *old, struct filedesc *fdp, struct proc *leader) diff --git a/sys/sys/filedesc.h b/sys/sys/filedesc.h --- a/sys/sys/filedesc.h +++ b/sys/sys/filedesc.h @@ -225,6 +225,7 @@ #define falloc(td, resultfp, resultfd, flags) \ falloc_caps(td, resultfp, resultfd, flags, NULL) +struct mount; struct thread; static __inline void @@ -241,6 +242,7 @@ int closef(struct file *fp, struct thread *td); void closef_nothread(struct file *fp); +int descrip_check_write_mp(struct filedesc *fdp, struct mount *mp); int dupfdopen(struct thread *td, struct filedesc *fdp, int dfd, int mode, int openerror, int *indxp); int falloc_caps(struct thread *td, struct file **resultfp, int *resultfd, diff --git a/sys/ufs/ffs/ffs_suspend.c b/sys/ufs/ffs/ffs_suspend.c --- a/sys/ufs/ffs/ffs_suspend.c +++ b/sys/ufs/ffs/ffs_suspend.c @@ -33,15 +33,15 @@ #include __FBSDID("$FreeBSD$"); -#include #include #include -#include -#include -#include #include +#include +#include #include +#include #include +#include #include @@ -303,6 +303,16 @@ vfs_rel(mp); if (error != 0) break; + + /* + * Require single-thread curproc so that the check is not racey. + * XXXKIB: might consider to singlethread curproc instead. + */ + error = curproc->p_numthreads > 1 ? EDEADLK : + descrip_check_write_mp(curproc->p_fd, mp); + if (error != 0) + break; + error = ffs_susp_suspend(mp); if (error != 0) { vfs_unbusy(mp);