diff --git a/sys/kern/vfs_bio.c b/sys/kern/vfs_bio.c --- a/sys/kern/vfs_bio.c +++ b/sys/kern/vfs_bio.c @@ -1718,7 +1718,7 @@ if (freebufs == bd->bd_lofreebuffers) bufspace_daemon_wakeup(bd); - error = BUF_LOCK(bp, LK_EXCLUSIVE, NULL); + error = BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL); KASSERT(error == 0, ("%s: BUF_LOCK on free buf %p: %d.", __func__, bp, error)); (void)error; diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c --- a/sys/ufs/ffs/ffs_alloc.c +++ b/sys/ufs/ffs/ffs_alloc.c @@ -81,6 +81,7 @@ #include #include #include +#include #include #include #include @@ -269,7 +270,9 @@ ump = ITOUMP(ip); fs = ump->um_fs; bp = NULL; - gbflags = (flags & BA_UNMAPPED) != 0 ? GB_UNMAPPED : 0; +retry_snap: + gbflags = ((flags & BA_UNMAPPED) != 0 ? GB_UNMAPPED : 0) | + (IS_SNAPSHOT(ip) ? GB_LOCK_NOWAIT : 0); mtx_assert(UFS_MTX(ump), MA_OWNED); #ifdef INVARIANTS @@ -303,9 +306,14 @@ * Allocate the extra space in the buffer. */ error = bread_gb(vp, lbprev, osize, NOCRED, gbflags, &bp); - if (error) { - return (error); + if (error == EBUSY && IS_SNAPSHOT(ip)) { + error = ffs_snap_relock(vp); + if (error == EBADF) + return (error); + goto retry_snap; } + if (error != 0) + return (error); if (bp->b_blkno == bp->b_lblkno) { if (lbprev >= UFS_NDADDR) diff --git a/sys/ufs/ffs/ffs_balloc.c b/sys/ufs/ffs/ffs_balloc.c --- a/sys/ufs/ffs/ffs_balloc.c +++ b/sys/ufs/ffs/ffs_balloc.c @@ -70,6 +70,7 @@ #include #include #include +#include #include #include @@ -82,6 +83,11 @@ #include #include +static int ffs_balloc_bread(struct vnode *vp, daddr_t lbn, int size, + struct ucred *cred, int gbflags, struct buf **bpp); +static int ffs_balloc_getblk(struct vnode *vp, daddr_t lbn, int size, + int gbflags, struct buf **bpp); + /* * Balloc defines the structure of filesystem storage * by allocating the physical blocks on a device given @@ -681,9 +687,9 @@ panic("ffs_balloc_ufs2: BA_METAONLY for ext block"); nb = dp->di_extb[lbn]; if (nb != 0 && dp->di_extsize >= smalllblktosize(fs, lbn + 1)) { - error = bread_gb(vp, -1 - lbn, fs->fs_bsize, NOCRED, - gbflags, &bp); - if (error) + error = ffs_balloc_bread(vp, -1 - lbn, fs->fs_bsize, + NOCRED, gbflags, &bp); + if (error != 0) goto done; bp->b_blkno = fsbtodb(fs, nb); bp->b_xflags |= BX_ALTDATA; @@ -697,9 +703,9 @@ osize = fragroundup(fs, blkoff(fs, dp->di_extsize)); nsize = fragroundup(fs, size); if (nsize <= osize) { - error = bread_gb(vp, -1 - lbn, osize, NOCRED, - gbflags, &bp); - if (error) + error = ffs_balloc_bread(vp, -1 - lbn, osize, + NOCRED, gbflags, &bp); + if (error != 0) goto done; bp->b_blkno = fsbtodb(fs, nb); bp->b_xflags |= BX_ALTDATA; @@ -723,13 +729,20 @@ nsize = fragroundup(fs, size); else nsize = fs->fs_bsize; + error = ffs_balloc_getblk(vp, -1 - lbn, nsize, gbflags, + &bp); + if (error != 0) + goto done; UFS_LOCK(ump); error = ffs_alloc(ip, lbn, ffs_blkpref_ufs2(ip, lbn, (int)lbn, &dp->di_extb[0]), nsize, flags, cred, &newb); - if (error) + if (error != 0) { + bp->b_flags |= B_INVAL | B_RELBUF | B_NOCACHE; + bp->b_flags &= ~(B_ASYNC | B_CACHE); + brelse(bp); goto done; - bp = getblk(vp, -1 - lbn, nsize, 0, 0, gbflags); + } bp->b_blkno = fsbtodb(fs, newb); bp->b_xflags |= BX_ALTDATA; if (flags & BA_CLRBUF) @@ -786,17 +799,15 @@ nb = dp->di_db[lbn]; if (nb != 0 && ip->i_size >= smalllblktosize(fs, lbn + 1)) { if ((flags & BA_CLRBUF) != 0) { - error = bread_gb(vp, lbn, fs->fs_bsize, NOCRED, - gbflags, &bp); + error = ffs_balloc_bread(vp, lbn, fs->fs_bsize, + NOCRED, gbflags, &bp); if (error != 0) goto done; } else { - bp = getblk(vp, lbn, fs->fs_bsize, 0, 0, - gbflags); - if (bp == NULL) { - error = EIO; + error = ffs_balloc_getblk(vp, lbn, + fs->fs_bsize, gbflags, &bp); + if (error != 0) goto done; - } vfs_bio_clrbuf(bp); } bp->b_blkno = fsbtodb(fs, nb); @@ -811,9 +822,9 @@ osize = fragroundup(fs, blkoff(fs, ip->i_size)); nsize = fragroundup(fs, size); if (nsize <= osize) { - error = bread_gb(vp, lbn, osize, NOCRED, + error = ffs_balloc_bread(vp, lbn, osize, NOCRED, gbflags, &bp); - if (error) + if (error != 0) goto done; bp->b_blkno = fsbtodb(fs, nb); } else { @@ -834,13 +845,19 @@ nsize = fragroundup(fs, size); else nsize = fs->fs_bsize; + error = ffs_balloc_getblk(vp, lbn, nsize, gbflags, &bp); + if (error != 0) + goto done; UFS_LOCK(ump); error = ffs_alloc(ip, lbn, ffs_blkpref_ufs2(ip, lbn, (int)lbn, - &dp->di_db[0]), nsize, flags, cred, &newb); - if (error) + &dp->di_db[0]), nsize, flags, cred, &newb); + if (error != 0) { + bp->b_flags |= B_INVAL | B_RELBUF | B_NOCACHE; + bp->b_flags &= ~(B_ASYNC | B_CACHE); + brelse(bp); goto done; - bp = getblk(vp, lbn, nsize, 0, 0, gbflags); + } bp->b_blkno = fsbtodb(fs, newb); if (flags & BA_CLRBUF) vfs_bio_clrbuf(bp); @@ -888,8 +905,10 @@ MPASS(lbns_remfree < lbns + nitems(lbns)); *allocblk++ = nb; *lbns_remfree++ = indirs[1].in_lbn; - bp = getblk(vp, indirs[1].in_lbn, fs->fs_bsize, 0, 0, - GB_UNMAPPED); + error = ffs_balloc_getblk(vp, indirs[1].in_lbn, fs->fs_bsize, + GB_UNMAPPED, &bp); + if (error != 0) + goto done; bp->b_blkno = fsbtodb(fs, nb); vfs_bio_clrbuf(bp); if (DOINGSOFTDEP(vp)) { @@ -914,11 +933,10 @@ */ retry: for (i = 1;;) { - error = bread(vp, - indirs[i].in_lbn, (int)fs->fs_bsize, NOCRED, &bp); - if (error) { + error = ffs_balloc_bread(vp, indirs[i].in_lbn, + (int)fs->fs_bsize, NOCRED, 0, &bp); + if (error != 0) goto fail; - } bap = (ufs2_daddr_t *)bp->b_data; nb = bap[indirs[i].in_off]; if ((error = UFS_CHECK_BLKNO(mp, ip->i_number, nb, @@ -969,8 +987,12 @@ MPASS(lbns_remfree < lbns + nitems(lbns)); *allocblk++ = nb; *lbns_remfree++ = indirs[i].in_lbn; - nbp = getblk(vp, indirs[i].in_lbn, fs->fs_bsize, 0, 0, - GB_UNMAPPED); + error = ffs_balloc_getblk(vp, indirs[i].in_lbn, fs->fs_bsize, + GB_UNMAPPED, &nbp); + if (error != 0) { + brelse(bp); + goto fail; + } nbp->b_blkno = fsbtodb(fs, nb); vfs_bio_clrbuf(nbp); if (DOINGSOFTDEP(vp)) { @@ -1054,7 +1076,9 @@ MPASS(lbns_remfree < lbns + nitems(lbns)); *allocblk++ = nb; *lbns_remfree++ = lbn; - nbp = getblk(vp, lbn, fs->fs_bsize, 0, 0, gbflags); + error = ffs_balloc_getblk(vp, lbn, fs->fs_bsize, gbflags, &nbp); + if (error != 0) + goto fail; nbp->b_blkno = fsbtodb(fs, nb); if (flags & BA_CLRBUF) vfs_bio_clrbuf(nbp); @@ -1091,10 +1115,11 @@ (vp->v_mount->mnt_flag & MNT_NOCLUSTERR) == 0 && !(vm_page_count_severe() || buf_dirty_count_severe())) { error = cluster_read(vp, ip->i_size, lbn, - (int)fs->fs_bsize, NOCRED, - MAXBSIZE, seqcount, gbflags, &nbp); + (int)fs->fs_bsize, NOCRED, MAXBSIZE, seqcount, + gbflags | (IS_SNAPSHOT(ip) ? GB_LOCK_NOWAIT : 0), + &nbp); } else { - error = bread_gb(vp, lbn, (int)fs->fs_bsize, + error = ffs_balloc_bread(vp, lbn, (int)fs->fs_bsize, NOCRED, gbflags, &nbp); } if (error) { @@ -1102,7 +1127,9 @@ goto fail; } } else { - nbp = getblk(vp, lbn, fs->fs_bsize, 0, 0, gbflags); + error = ffs_balloc_getblk(vp, lbn, fs->fs_bsize, gbflags, &nbp); + if (error != 0) + goto fail; nbp->b_blkno = fsbtodb(fs, nb); } curthread_pflags_restore(saved_inbdflush); @@ -1131,15 +1158,16 @@ * XXX Still have to journal the free below */ (void) ffs_syncvnode(vp, MNT_WAIT, 0); + for (deallocated = 0, blkp = allociblk, lbns_remfree = lbns; blkp < allocblk; blkp++, lbns_remfree++) { /* * We shall not leave the freed blocks on the vnode * buffer object lists. */ - bp = getblk(vp, *lbns_remfree, fs->fs_bsize, 0, 0, - GB_NOCREAT | GB_UNMAPPED); - if (bp != NULL) { + error = ffs_balloc_getblk(vp, *lbns_remfree, fs->fs_bsize, + GB_NOCREAT | GB_UNMAPPED, &bp); + if (error == 0) { KASSERT(bp->b_blkno == fsbtodb(fs, *blkp), ("mismatch2 l %jd %jd b %ju %ju", (intmax_t)bp->b_lblkno, (uintmax_t)*lbns_remfree, @@ -1148,6 +1176,10 @@ bp->b_flags |= B_INVAL | B_RELBUF | B_NOCACHE; bp->b_flags &= ~(B_ASYNC | B_CACHE); brelse(bp); + } else if (error != EEXIST) { + /* XXXKIB what to do? */ + printf("Can not getblk() to deallocate block, error %d", + error); } deallocated += fs->fs_bsize; } @@ -1156,10 +1188,10 @@ } else if (unwindidx >= 0) { int r; - r = bread(vp, indirs[unwindidx].in_lbn, - (int)fs->fs_bsize, NOCRED, &bp); - if (r) { - panic("Could not unwind indirect block, error %d", r); + r = ffs_balloc_bread(vp, indirs[unwindidx].in_lbn, + (int)fs->fs_bsize, NOCRED, 0, &bp); + if (r != 0) { + printf("Could not unwind indirect block, error %d", r); brelse(bp); } else { bap = (ufs2_daddr_t *)bp->b_data; @@ -1192,9 +1224,9 @@ #ifdef INVARIANTS if (blkp == allociblk) lbns_remfree = lbns; - bp = getblk(vp, *lbns_remfree, fs->fs_bsize, 0, 0, - GB_NOCREAT | GB_UNMAPPED); - if (bp != NULL) { + error = ffs_balloc_getblk(vp, *lbns_remfree, fs->fs_bsize, + GB_NOCREAT | GB_UNMAPPED, &bp); + if (error == 0) { panic("zombie2 %jd %ju %ju", (intmax_t)bp->b_lblkno, (uintmax_t)bp->b_blkno, (uintmax_t)fsbtodb(fs, *blkp)); @@ -1208,3 +1240,39 @@ vn_seqc_write_end(vp); return (error); } + +static int +ffs_balloc_bread(struct vnode *vp, daddr_t lbn, int size, struct ucred *cred, + int gbflags, struct buf **bpp) +{ + int error; + + for (;;) { + error = bread_gb(vp, lbn, size, cred, gbflags | + (IS_SNAPSHOT(VTOI(vp)) ? GB_LOCK_NOWAIT : 0), bpp); + if (error == 0) + return (0); + if (error == EBUSY && IS_SNAPSHOT(VTOI(vp))) + error = ffs_snap_relock(vp); + if (error != 0) + return (error); + } +} + +static int +ffs_balloc_getblk(struct vnode *vp, daddr_t lbn, int size, int gbflags, + struct buf **bpp) +{ + int error; + + for (;;) { + error = getblkx(vp, lbn, 0, size, 0, 0, gbflags | + (IS_SNAPSHOT(VTOI(vp)) ? GB_LOCK_NOWAIT : 0), bpp); + if (error == 0) + return (0); + if (error == EBUSY && IS_SNAPSHOT(VTOI(vp))) + error = ffs_snap_relock(vp); + if (error != 0) + return (error); + } +} diff --git a/sys/ufs/ffs/ffs_extern.h b/sys/ufs/ffs/ffs_extern.h --- a/sys/ufs/ffs/ffs_extern.h +++ b/sys/ufs/ffs/ffs_extern.h @@ -99,6 +99,7 @@ int ffs_snapblkfree(struct fs *, struct vnode *, ufs2_daddr_t, long, ino_t, enum vtype, struct workhead *); void ffs_snapremove(struct vnode *vp); +int ffs_snap_relock(struct vnode *vp); int ffs_snapshot(struct mount *mp, char *snapfile); void ffs_snapshot_mount(struct mount *mp); void ffs_snapshot_unmount(struct mount *mp); diff --git a/sys/ufs/ffs/ffs_inode.c b/sys/ufs/ffs/ffs_inode.c --- a/sys/ufs/ffs/ffs_inode.c +++ b/sys/ufs/ffs/ffs_inode.c @@ -161,25 +161,8 @@ if (error != EBUSY || (flags & GB_LOCK_NOWAIT) == 0) return (error); - /* - * Wait for our inode block to become available. - * - * Hold a reference to the vnode to protect against - * ffs_snapgone(). Since we hold a reference, it can only - * get reclaimed (VIRF_DOOMED flag) in a forcible downgrade - * or unmount. For an unmount, the entire filesystem will be - * gone, so we cannot attempt to touch anything associated - * with it while the vnode is unlocked; all we can do is - * pause briefly and try again. If when we relock the vnode - * we discover that it has been reclaimed, updating it is no - * longer necessary and we can just return an error. - */ - vref(vp); - VOP_UNLOCK(vp); - pause("ffsupd", 1); - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); - vrele(vp); - if (VN_IS_DOOMED(vp)) + error = ffs_snap_relock(vp); + if (error == EBADF) return (ENOENT); /* @@ -188,9 +171,11 @@ */ goto loop; } - if (DOINGSOFTDEP(vp)) + if (DOINGSOFTDEP(vp)) { softdep_update_inodeblock(ip, bp, waitfor); - else if (ip->i_effnlink != ip->i_nlink) + if (!IS_UFS(vp)) + return (ENOENT); + } else if (ip->i_effnlink != ip->i_nlink) panic("ffs_update: bad link cnt"); if (I_IS_UFS1(ip)) { *((struct ufs1_dinode *)bp->b_data + diff --git a/sys/ufs/ffs/ffs_snapshot.c b/sys/ufs/ffs/ffs_snapshot.c --- a/sys/ufs/ffs/ffs_snapshot.c +++ b/sys/ufs/ffs/ffs_snapshot.c @@ -139,6 +139,12 @@ { } +int +ffs_snap_relock(struct vnode *vp __unused) +{ + return (0); +} + #else FEATURE(ffs_snapshot, "FFS snapshot support"); @@ -2745,4 +2751,33 @@ return (sn); } +/* + * This function should be used after bread_gb(GB_LOCK_NOWAIT) + * returned EBUSY, when caller owns the snapshot lock. It temporarily + * unlocks the snapshot vnode to allow for the block to become + * available. + */ +int +ffs_snap_relock(struct vnode *vp) +{ + int locked; + + MPASS(IS_SNAPSHOT(VTOI(vp))); + + /* + * Hold a reference to the vnode to protect against + * ffs_snapgone(), in case caller does not. + */ + vref(vp); + + locked = VOP_ISLOCKED(vp); + VOP_UNLOCK(vp); + pause("slkrd", 1); + vn_lock(vp, locked | LK_RETRY); + vrele(vp); + if (!IS_UFS(vp)) + return (EBADF); + return (0); +} + #endif diff --git a/sys/ufs/ffs/ffs_softdep.c b/sys/ufs/ffs/ffs_softdep.c --- a/sys/ufs/ffs/ffs_softdep.c +++ b/sys/ufs/ffs/ffs_softdep.c @@ -762,7 +762,8 @@ static int softdep_process_worklist(struct mount *, int); static int softdep_waitidle(struct mount *, int); static void drain_output(struct vnode *); -static struct buf *getdirtybuf(struct buf *, struct rwlock *, int); +static struct buf *getdirtybuf(struct inode *, struct buf *, + struct rwlock *, int); static int check_inodedep_free(struct inodedep *); static void clear_remove(struct mount *); static void clear_inodedeps(struct mount *); @@ -774,8 +775,9 @@ static int free_pagedep(struct pagedep *); static int flush_newblk_dep(struct vnode *, struct mount *, ufs_lbn_t); static int flush_inodedep_deps(struct vnode *, struct mount *, ino_t); -static int flush_deplist(struct allocdirectlst *, int, int *); -static int sync_cgs(struct mount *, int); +static int flush_deplist(struct vnode *, struct allocdirectlst *, + int, int *); +static int sync_cgs(struct vnode *, struct mount *, int); static int handle_written_filepage(struct pagedep *, struct buf *, int); static int handle_written_sbdep(struct sbdep *, struct buf *); static void initiate_write_sbdep(struct sbdep *); @@ -1947,7 +1949,7 @@ } if (cgwait) { FREE_LOCK(ump); - sync_cgs(mp, MNT_WAIT); + sync_cgs(vp, mp, MNT_WAIT); ffs_sync_snap(mp, MNT_WAIT); ACQUIRE_LOCK(ump); continue; @@ -6757,7 +6759,12 @@ /* See if the block was discarded. */ if (bp == NULL) break; - /* Inline part of getdirtybuf(). We dont want bremfree. */ + + /* + * Inline part of getdirtybuf(). We dont want + * bremfree. There is no vnode locked there, so the + * order of snap lock->buffer lock is not possible. + */ if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL) == 0) break; if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_SLEEPFAIL | LK_INTERLOCK, @@ -7077,6 +7084,10 @@ if (bp->b_bufsize == fs->fs_bsize) bp->b_flags |= B_CLUSTEROK; softdep_update_inodeblock(ip, bp, 0); + if (!IS_UFS(vp)) { + brelse(bp); + return; + } if (ump->um_fstype == UFS1) { *((struct ufs1_dinode *)bp->b_data + ino_to_fsbo(fs, ip->i_number)) = *ip->i_din1; @@ -7527,6 +7538,8 @@ TAILQ_FOREACH(bp, &bo->bo_dirty.bv_hd, b_bobufs) bp->b_vflags &= ~BV_SCANNED; restart: + if (!IS_UFS(vp)) + return; TAILQ_FOREACH(bp, &bo->bo_dirty.bv_hd, b_bobufs) { if (bp->b_vflags & BV_SCANNED) continue; @@ -7535,7 +7548,8 @@ continue; } KASSERT(bp->b_bufobj == bo, ("Wrong object in buffer")); - if ((bp = getdirtybuf(bp, BO_LOCKPTR(bo), MNT_WAIT)) == NULL) + if ((bp = getdirtybuf(ip, bp, BO_LOCKPTR(bo), + MNT_WAIT)) == NULL) goto restart; BO_UNLOCK(bo); if (deallocate_dependencies(bp, freeblks, blkoff)) @@ -12709,9 +12723,11 @@ struct mount *mp; struct buf *ibp; struct fs *fs; + struct vnode *vp; int error; ump = ITOUMP(ip); + vp = ITOV(ip); mp = UFSTOVFS(ump); KASSERT(MOUNTEDSOFTDEP(mp) != 0, ("softdep_update_inodeblock called on non-softdep filesystem")); @@ -12810,14 +12826,15 @@ return; } ibp = inodedep->id_bmsafemap->sm_buf; - ibp = getdirtybuf(ibp, LOCK_PTR(ump), MNT_WAIT); + ibp = getdirtybuf(ip, ibp, LOCK_PTR(ump), MNT_WAIT); if (ibp == NULL) { /* * If ibp came back as NULL, the dependency could have been * freed while we slept. Look it up again, and check to see * that it has completed. */ - if (inodedep_lookup(mp, ip->i_number, 0, &inodedep) != 0) + if (IS_UFS(vp) && inodedep_lookup(mp, ip->i_number, 0, + &inodedep) != 0) goto retry; FREE_LOCK(ump); return; @@ -13074,7 +13091,8 @@ * indirect block. */ static int -sync_cgs(mp, waitfor) +sync_cgs(vp, mp, waitfor) + struct vnode *vp; struct mount *mp; int waitfor; { @@ -13104,7 +13122,8 @@ * If we don't get the lock and we're waiting try again, if * not move on to the next buf and try to sync it. */ - bp = getdirtybuf(bmsafemap->sm_buf, LOCK_PTR(ump), waitfor); + bp = getdirtybuf(VTOI(vp), bmsafemap->sm_buf, LOCK_PTR(ump), + waitfor); if (bp == NULL && waitfor == MNT_WAIT) continue; LIST_REMOVE(sentinel, sm_next); @@ -13216,7 +13235,12 @@ waitfor == MNT_NOWAIT) continue; nbp = newblk->nb_bmsafemap->sm_buf; - nbp = getdirtybuf(nbp, LOCK_PTR(ump), waitfor); + nbp = getdirtybuf(VTOI(vp), nbp, LOCK_PTR(ump), + waitfor); + if (!IS_UFS(vp)) { + error = EBADF; + goto out; + } if (nbp == NULL) goto top; FREE_LOCK(ump); @@ -13247,7 +13271,12 @@ if (newblk->nb_state & DEPCOMPLETE) continue; nbp = newblk->nb_bmsafemap->sm_buf; - nbp = getdirtybuf(nbp, LOCK_PTR(ump), waitfor); + nbp = getdirtybuf(VTOI(vp), nbp, LOCK_PTR(ump), + waitfor); + if (!IS_UFS(vp)) { + error = EBADF; + goto out; + } if (nbp == NULL) goto restart; FREE_LOCK(ump); @@ -13355,11 +13384,30 @@ goto restart; } } - if (flush_deplist(&inodedep->id_inoupdt, waitfor, &error) || - flush_deplist(&inodedep->id_newinoupdt, waitfor, &error) || - flush_deplist(&inodedep->id_extupdt, waitfor, &error) || - flush_deplist(&inodedep->id_newextupdt, waitfor, &error)) + if (flush_deplist(vp, &inodedep->id_inoupdt, waitfor, + &error)) { + if (!IS_UFS(vp)) + return (EBADF); + continue; + } + if (flush_deplist(vp, &inodedep->id_newinoupdt, waitfor, + &error)) { + if (!IS_UFS(vp)) + return (EBADF); + continue; + } + if (flush_deplist(vp, &inodedep->id_extupdt, waitfor, + &error)) { + if (!IS_UFS(vp)) + return (EBADF); + continue; + } + if (flush_deplist(vp, &inodedep->id_newextupdt, waitfor, + &error)) { + if (!IS_UFS(vp)) + return (EBADF); continue; + } /* * If pass2, we are done, otherwise do pass 2. */ @@ -13379,7 +13427,8 @@ * Flush an inode dependency list. */ static int -flush_deplist(listhead, waitfor, errorp) +flush_deplist(vp, listhead, waitfor, errorp) + struct vnode *vp; struct allocdirectlst *listhead; int waitfor; int *errorp; @@ -13402,7 +13451,9 @@ if (newblk->nb_state & DEPCOMPLETE) continue; bp = newblk->nb_bmsafemap->sm_buf; - bp = getdirtybuf(bp, LOCK_PTR(ump), waitfor); + bp = getdirtybuf(VTOI(vp), bp, LOCK_PTR(ump), waitfor); + if (!IS_UFS(vp)) + return (1); if (bp == NULL) { if (waitfor == MNT_NOWAIT) continue; @@ -13468,7 +13519,11 @@ */ if ((newblk->nb_state & DEPCOMPLETE) == 0) { bp = newblk->nb_bmsafemap->sm_buf; - bp = getdirtybuf(bp, LOCK_PTR(ump), MNT_WAIT); + bp = getdirtybuf(ip, bp, LOCK_PTR(ump), MNT_WAIT); + if (!IS_UFS(vp)) { + FREE_LOCK(ump); + return (EBADF); + } if (bp == NULL) continue; FREE_LOCK(ump); @@ -13639,7 +13694,8 @@ */ if ((inodedep->id_state & (DEPCOMPLETE | GOINGAWAY)) == 0) { bp = inodedep->id_bmsafemap->sm_buf; - bp = getdirtybuf(bp, LOCK_PTR(ump), MNT_WAIT); + /* directory cannot be a snapshot */ + bp = getdirtybuf(NULL, bp, LOCK_PTR(ump), MNT_WAIT); if (bp == NULL) goto retry; FREE_LOCK(ump); @@ -14710,7 +14766,8 @@ * Return acquired buffer or NULL on failure. */ static struct buf * -getdirtybuf(bp, lock, waitfor) +getdirtybuf(ip, bp, lock, waitfor) + struct inode *ip; struct buf *bp; struct rwlock *lock; int waitfor; @@ -14720,8 +14777,14 @@ if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL) != 0) { if (waitfor != MNT_WAIT) return (NULL); - error = BUF_LOCK(bp, - LK_EXCLUSIVE | LK_SLEEPFAIL | LK_INTERLOCK, lock); + if (ip != NULL && IS_SNAPSHOT(ip)) { + rw_wunlock(lock); + (void)ffs_snap_relock(ITOV(ip)); + rw_wlock(lock); + return (NULL); + } + error = BUF_LOCK(bp, LK_EXCLUSIVE | LK_SLEEPFAIL | + LK_INTERLOCK, lock); /* * Even if we successfully acquire bp here, we have dropped * lock, which may violates our guarantee. diff --git a/sys/ufs/ffs/ffs_vnops.c b/sys/ufs/ffs/ffs_vnops.c --- a/sys/ufs/ffs/ffs_vnops.c +++ b/sys/ufs/ffs/ffs_vnops.c @@ -690,7 +690,9 @@ uio->uio_offset >= fs->fs_maxfilesize) return (EOVERFLOW); - bflag = GB_UNMAPPED | (uio->uio_segflg == UIO_NOCOPY ? 0 : GB_NOSPARSE); + bflag = GB_UNMAPPED | + (uio->uio_segflg == UIO_NOCOPY ? 0 : GB_NOSPARSE) | + (IS_SNAPSHOT(ip) ? GB_LOCK_NOWAIT : 0); for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) { if ((bytesinfile = ip->i_size - uio->uio_offset) <= 0) break; @@ -759,6 +761,12 @@ */ error = bread_gb(vp, lbn, size, NOCRED, bflag, &bp); } + if (error == EBUSY && IS_SNAPSHOT(ip)) { + error = ffs_snap_relock(vp); + if (error == EBADF) + break; + continue; + } if (error == EJUSTRETURN) { error = ffs_read_hole(uio, xfersize, &size); if (error == 0) @@ -2066,7 +2074,7 @@ * and respond to dead vnodes by returning ESTALE. */ VOP_LOCK(vp, vp_locked | LK_RETRY); - if (!VN_IS_DOOMED(vp)) + if (IS_UFS(vp)) return (0); /* diff --git a/sys/ufs/ufs/inode.h b/sys/ufs/ufs/inode.h --- a/sys/ufs/ufs/inode.h +++ b/sys/ufs/ufs/inode.h @@ -247,6 +247,7 @@ } while (0) #define IS_SNAPSHOT(ip) ((ip)->i_flags & SF_SNAPSHOT) +#define IS_UFS(vp) ((vp)->v_data != NULL) /* * Structure used to pass around logical block paths generated by