diff --git a/sys/ufs/ffs/ffs_extern.h b/sys/ufs/ffs/ffs_extern.h index c29e5d5..d028ac3 100644 --- a/sys/ufs/ffs/ffs_extern.h +++ b/sys/ufs/ffs/ffs_extern.h @@ -180,12 +180,19 @@ int ffs_rdonly(struct inode *); TAILQ_HEAD(snaphead, inode); +/* + * sn_flags. + */ +#define SN_DRAIN 1 + struct snapdata { LIST_ENTRY(snapdata) sn_link; struct snaphead sn_head; daddr_t sn_listsize; daddr_t *sn_blklist; struct lock sn_lock; + int sn_lock_refcount; + int sn_flags; }; #endif /* _KERNEL */ diff --git a/sys/ufs/ffs/ffs_snapshot.c b/sys/ufs/ffs/ffs_snapshot.c index 3df8932..3b8f45c 100644 --- a/sys/ufs/ffs/ffs_snapshot.c +++ b/sys/ufs/ffs/ffs_snapshot.c @@ -193,6 +193,24 @@ SYSCTL_INT(_debug, OID_AUTO, collectsnapstats, CTLFLAG_RW, &collectsnapstats, 0, ""); #endif /* DEBUG */ +static inline void +ffs_snapshot_refcount_inc(struct snapdata *sn) +{ + + sn->sn_lock_refcount++; +} + +static inline void +ffs_snapshot_refcount_dec(struct snapdata *sn) +{ + + sn->sn_lock_refcount--; + if (sn->sn_lock_refcount == 0 && (sn->sn_flags & SN_DRAIN)) { + sn->sn_flags &= ~SN_DRAIN; + wakeup(&sn->sn_lock_refcount); + } +} + /* * Create a snapshot file and initialize it for the filesystem. */ @@ -1630,6 +1648,7 @@ ffs_snapremove(vp) ("ffs_snapremove: lost lock mutation")); vp->v_vnlock = &vp->v_lock; VI_LOCK(devvp); + ffs_snapshot_refcount_dec(sn); lockmgr(&sn->sn_lock, LK_RELEASE, NULL); try_free_snapdata(devvp); } else @@ -1753,16 +1772,20 @@ ffs_snapblkfree(fs, devvp, bno, size, inum, vtype, wkhd) struct snapdata *sn; lbn = fragstoblks(fs, bno); -retry: VI_LOCK(devvp); +retry: sn = devvp->v_rdev->si_snapdata; if (sn == NULL) { VI_UNLOCK(devvp); return (0); } + ffs_snapshot_refcount_inc(sn); if (lockmgr(&sn->sn_lock, LK_INTERLOCK | LK_EXCLUSIVE | LK_SLEEPFAIL, - VI_MTX(devvp)) != 0) + VI_MTX(devvp)) != 0) { + VI_LOCK(devvp); + ffs_snapshot_refcount_dec(sn); goto retry; + } TAILQ_FOREACH(ip, &sn->sn_head, i_nextsnap) { vp = ITOV(ip); if (DOINGSOFTDEP(vp)) @@ -1862,6 +1885,7 @@ retry: } DIP_SET(ip, i_blocks, DIP(ip, i_blocks) + btodb(size)); ip->i_flag |= IN_CHANGE | IN_UPDATE; + ffs_snapshot_refcount_dec(sn); lockmgr(vp->v_vnlock, LK_RELEASE, NULL); return (1); } @@ -1939,6 +1963,7 @@ retry: */ if (error != 0 && wkhd != NULL) softdep_freework(wkhd); + ffs_snapshot_refcount_dec(sn); lockmgr(vp->v_vnlock, LK_RELEASE, NULL); return (error); } @@ -2017,6 +2042,7 @@ ffs_snapshot_mount(mp) * original private lock. */ vp->v_vnlock = &sn->sn_lock; + ffs_snapshot_refcount_dec(sn); lockmgr(&vp->v_lock, LK_RELEASE, NULL); /* * Link it onto the active snapshot list. @@ -2098,6 +2124,7 @@ ffs_snapshot_unmount(mp) vp = ITOV(xp); TAILQ_REMOVE(&sn->sn_head, xp, i_nextsnap); xp->i_nextsnap.tqe_prev = 0; + ffs_snapshot_refcount_inc(sn); lockmgr(&sn->sn_lock, LK_INTERLOCK | LK_EXCLUSIVE, VI_MTX(devvp)); lockmgr(&vp->v_lock, LK_EXCLUSIVE, NULL); @@ -2105,6 +2132,7 @@ ffs_snapshot_unmount(mp) ("ffs_snapshot_unmount: lost lock mutation")); vp->v_vnlock = &vp->v_lock; lockmgr(&vp->v_lock, LK_RELEASE, NULL); + ffs_snapshot_refcount_dec(sn); lockmgr(&sn->sn_lock, LK_RELEASE, NULL); if (xp->i_effnlink > 0) vrele(vp); @@ -2299,6 +2327,7 @@ ffs_copyonwrite(devvp, bp) sn = devvp->v_rdev->si_snapdata; if (sn == NULL || TAILQ_EMPTY(&sn->sn_head)) { + ffs_snapshot_refcount_dec(sn); VI_UNLOCK(devvp); if (saved_runningbufspace != 0) { bp->b_runningbufspace = saved_runningbufspace; @@ -2468,15 +2497,19 @@ ffs_sync_snap(mp, waitfor) VI_UNLOCK(devvp); return; } + ffs_snapshot_refcount_inc(sn); if (lockmgr(&sn->sn_lock, LK_INTERLOCK | LK_EXCLUSIVE | LK_SLEEPFAIL, VI_MTX(devvp)) == 0) break; + else + ffs_snapshot_refcount_dec(sn); } TAILQ_FOREACH(ip, &sn->sn_head, i_nextsnap) { vp = ITOV(ip); ffs_syncvnode(vp, waitfor, NO_INO_UPDT); } + ffs_snapshot_refcount_dec(sn); lockmgr(&sn->sn_lock, LK_RELEASE, NULL); } @@ -2591,6 +2624,8 @@ ffs_snapdata_alloc(void) TAILQ_INIT(&sn->sn_head); lockinit(&sn->sn_lock, PVFS, "snaplk", VLKTIMEOUT, LK_CANRECURSE | LK_NOSHARE); + sn->sn_lock_refcount = 0; + sn->sn_flags = 0; return (sn); } @@ -2613,6 +2648,7 @@ try_free_snapdata(struct vnode *devvp) { struct snapdata *sn; ufs2_daddr_t *snapblklist; + int error; ASSERT_VI_LOCKED(devvp, "try_free_snapdata"); sn = devvp->v_rdev->si_snapdata; @@ -2623,13 +2659,22 @@ try_free_snapdata(struct vnode *devvp) return; } + error = 0; + KASSERT(sn->sn_lock_refcount >= 0, + ("snapshot lock refcount can't be negative")); + if (sn->sn_lock_refcount > 0) { + sn->sn_flags |= SN_DRAIN; + error = msleep(&sn->sn_lock_refcount, VI_MTX(devvp), PVFS, + "snapshot lock drain", 0); + } else + VI_UNLOCK(devvp); + KASSERT(error == 0, ("%s: msleep can't return %d in the drain path" + "@ %s:%d", __func__, error, __FILE__, __LINE__)); devvp->v_rdev->si_snapdata = NULL; devvp->v_vflag &= ~VV_COPYONWRITE; - lockmgr(&sn->sn_lock, LK_DRAIN|LK_INTERLOCK, VI_MTX(devvp)); snapblklist = sn->sn_blklist; sn->sn_blklist = NULL; sn->sn_listsize = 0; - lockmgr(&sn->sn_lock, LK_RELEASE, NULL); if (snapblklist != NULL) free(snapblklist, M_UFSMNT); ffs_snapdata_free(sn); @@ -2662,6 +2707,7 @@ ffs_snapdata_acquire(struct vnode *devvp) /* * Acquire the snapshot lock. */ + ffs_snapshot_refcount_inc(sn); lockmgr(&sn->sn_lock, LK_INTERLOCK | LK_EXCLUSIVE | LK_RETRY, VI_MTX(devvp)); /*