Changeset View
Changeset View
Standalone View
Standalone View
sys/ufs/ffs/ffs_vnops.c
Show First 20 Lines • Show All 97 Lines • ▼ Show 20 Lines | |||||
#include <ufs/ffs/ffs_extern.h> | #include <ufs/ffs/ffs_extern.h> | ||||
#include "opt_directio.h" | #include "opt_directio.h" | ||||
#include "opt_ffs.h" | #include "opt_ffs.h" | ||||
#ifdef DIRECTIO | #ifdef DIRECTIO | ||||
extern int ffs_rawread(struct vnode *vp, struct uio *uio, int *workdone); | extern int ffs_rawread(struct vnode *vp, struct uio *uio, int *workdone); | ||||
#endif | #endif | ||||
static vop_fsync_t ffs_fsync; | static vop_fsync_t ffs_fsync; | ||||
static vop_fdatasync_t ffs_fdatasync; | |||||
static vop_lock1_t ffs_lock; | static vop_lock1_t ffs_lock; | ||||
static vop_read_t ffs_read; | static vop_read_t ffs_read; | ||||
static vop_write_t ffs_write; | static vop_write_t ffs_write; | ||||
static int ffs_extread(struct vnode *vp, struct uio *uio, int ioflag); | static int ffs_extread(struct vnode *vp, struct uio *uio, int ioflag); | ||||
static int ffs_extwrite(struct vnode *vp, struct uio *uio, int ioflag, | static int ffs_extwrite(struct vnode *vp, struct uio *uio, int ioflag, | ||||
struct ucred *cred); | struct ucred *cred); | ||||
static vop_strategy_t ffsext_strategy; | static vop_strategy_t ffsext_strategy; | ||||
static vop_closeextattr_t ffs_closeextattr; | static vop_closeextattr_t ffs_closeextattr; | ||||
static vop_deleteextattr_t ffs_deleteextattr; | static vop_deleteextattr_t ffs_deleteextattr; | ||||
static vop_getextattr_t ffs_getextattr; | static vop_getextattr_t ffs_getextattr; | ||||
static vop_listextattr_t ffs_listextattr; | static vop_listextattr_t ffs_listextattr; | ||||
static vop_openextattr_t ffs_openextattr; | static vop_openextattr_t ffs_openextattr; | ||||
static vop_setextattr_t ffs_setextattr; | static vop_setextattr_t ffs_setextattr; | ||||
static vop_vptofh_t ffs_vptofh; | static vop_vptofh_t ffs_vptofh; | ||||
/* Global vfs data structures for ufs. */ | /* Global vfs data structures for ufs. */ | ||||
struct vop_vector ffs_vnodeops1 = { | struct vop_vector ffs_vnodeops1 = { | ||||
.vop_default = &ufs_vnodeops, | .vop_default = &ufs_vnodeops, | ||||
.vop_fsync = ffs_fsync, | .vop_fsync = ffs_fsync, | ||||
.vop_fdatasync = ffs_fdatasync, | |||||
.vop_getpages = vnode_pager_local_getpages, | .vop_getpages = vnode_pager_local_getpages, | ||||
.vop_getpages_async = vnode_pager_local_getpages_async, | .vop_getpages_async = vnode_pager_local_getpages_async, | ||||
.vop_lock1 = ffs_lock, | .vop_lock1 = ffs_lock, | ||||
.vop_read = ffs_read, | .vop_read = ffs_read, | ||||
.vop_reallocblks = ffs_reallocblks, | .vop_reallocblks = ffs_reallocblks, | ||||
.vop_write = ffs_write, | .vop_write = ffs_write, | ||||
.vop_vptofh = ffs_vptofh, | .vop_vptofh = ffs_vptofh, | ||||
}; | }; | ||||
struct vop_vector ffs_fifoops1 = { | struct vop_vector ffs_fifoops1 = { | ||||
.vop_default = &ufs_fifoops, | .vop_default = &ufs_fifoops, | ||||
.vop_fsync = ffs_fsync, | .vop_fsync = ffs_fsync, | ||||
.vop_fdatasync = ffs_fdatasync, | |||||
.vop_reallocblks = ffs_reallocblks, /* XXX: really ??? */ | .vop_reallocblks = ffs_reallocblks, /* XXX: really ??? */ | ||||
.vop_vptofh = ffs_vptofh, | .vop_vptofh = ffs_vptofh, | ||||
}; | }; | ||||
/* Global vfs data structures for ufs. */ | /* Global vfs data structures for ufs. */ | ||||
struct vop_vector ffs_vnodeops2 = { | struct vop_vector ffs_vnodeops2 = { | ||||
.vop_default = &ufs_vnodeops, | .vop_default = &ufs_vnodeops, | ||||
.vop_fsync = ffs_fsync, | .vop_fsync = ffs_fsync, | ||||
.vop_fdatasync = ffs_fdatasync, | |||||
.vop_getpages = vnode_pager_local_getpages, | .vop_getpages = vnode_pager_local_getpages, | ||||
.vop_getpages_async = vnode_pager_local_getpages_async, | .vop_getpages_async = vnode_pager_local_getpages_async, | ||||
.vop_lock1 = ffs_lock, | .vop_lock1 = ffs_lock, | ||||
.vop_read = ffs_read, | .vop_read = ffs_read, | ||||
.vop_reallocblks = ffs_reallocblks, | .vop_reallocblks = ffs_reallocblks, | ||||
.vop_write = ffs_write, | .vop_write = ffs_write, | ||||
.vop_closeextattr = ffs_closeextattr, | .vop_closeextattr = ffs_closeextattr, | ||||
.vop_deleteextattr = ffs_deleteextattr, | .vop_deleteextattr = ffs_deleteextattr, | ||||
.vop_getextattr = ffs_getextattr, | .vop_getextattr = ffs_getextattr, | ||||
.vop_listextattr = ffs_listextattr, | .vop_listextattr = ffs_listextattr, | ||||
.vop_openextattr = ffs_openextattr, | .vop_openextattr = ffs_openextattr, | ||||
.vop_setextattr = ffs_setextattr, | .vop_setextattr = ffs_setextattr, | ||||
.vop_vptofh = ffs_vptofh, | .vop_vptofh = ffs_vptofh, | ||||
}; | }; | ||||
struct vop_vector ffs_fifoops2 = { | struct vop_vector ffs_fifoops2 = { | ||||
.vop_default = &ufs_fifoops, | .vop_default = &ufs_fifoops, | ||||
.vop_fsync = ffs_fsync, | .vop_fsync = ffs_fsync, | ||||
.vop_fdatasync = ffs_fdatasync, | |||||
.vop_lock1 = ffs_lock, | .vop_lock1 = ffs_lock, | ||||
.vop_reallocblks = ffs_reallocblks, | .vop_reallocblks = ffs_reallocblks, | ||||
.vop_strategy = ffsext_strategy, | .vop_strategy = ffsext_strategy, | ||||
.vop_closeextattr = ffs_closeextattr, | .vop_closeextattr = ffs_closeextattr, | ||||
.vop_deleteextattr = ffs_deleteextattr, | .vop_deleteextattr = ffs_deleteextattr, | ||||
.vop_getextattr = ffs_getextattr, | .vop_getextattr = ffs_getextattr, | ||||
.vop_listextattr = ffs_listextattr, | .vop_listextattr = ffs_listextattr, | ||||
.vop_openextattr = ffs_openextattr, | .vop_openextattr = ffs_openextattr, | ||||
Show All 39 Lines | retry: | ||||
return (0); | return (0); | ||||
} | } | ||||
int | int | ||||
ffs_syncvnode(struct vnode *vp, int waitfor, int flags) | ffs_syncvnode(struct vnode *vp, int waitfor, int flags) | ||||
{ | { | ||||
struct inode *ip; | struct inode *ip; | ||||
struct bufobj *bo; | struct bufobj *bo; | ||||
struct buf *bp; | struct buf *bp, *nbp; | ||||
struct buf *nbp; | |||||
ufs_lbn_t lbn; | ufs_lbn_t lbn; | ||||
int error, wait, passes; | int error, passes; | ||||
bool still_dirty, wait; | |||||
ip = VTOI(vp); | ip = VTOI(vp); | ||||
ip->i_flag &= ~IN_NEEDSYNC; | ip->i_flag &= ~IN_NEEDSYNC; | ||||
bo = &vp->v_bufobj; | bo = &vp->v_bufobj; | ||||
/* | /* | ||||
* When doing MNT_WAIT we must first flush all dependencies | * When doing MNT_WAIT we must first flush all dependencies | ||||
* on the inode. | * on the inode. | ||||
*/ | */ | ||||
if (DOINGSOFTDEP(vp) && waitfor == MNT_WAIT && | if (DOINGSOFTDEP(vp) && waitfor == MNT_WAIT && | ||||
(error = softdep_sync_metadata(vp)) != 0) | (error = softdep_sync_metadata(vp)) != 0) | ||||
return (error); | return (error); | ||||
/* | /* | ||||
* Flush all dirty buffers associated with a vnode. | * Flush all dirty buffers associated with a vnode. | ||||
*/ | */ | ||||
error = 0; | error = 0; | ||||
passes = 0; | passes = 0; | ||||
wait = 0; /* Always do an async pass first. */ | wait = false; /* Always do an async pass first. */ | ||||
lbn = lblkno(ip->i_fs, (ip->i_size + ip->i_fs->fs_bsize - 1)); | lbn = lblkno(ip->i_fs, (ip->i_size + ip->i_fs->fs_bsize - 1)); | ||||
BO_LOCK(bo); | BO_LOCK(bo); | ||||
loop: | loop: | ||||
TAILQ_FOREACH(bp, &bo->bo_dirty.bv_hd, b_bobufs) | TAILQ_FOREACH(bp, &bo->bo_dirty.bv_hd, b_bobufs) | ||||
bp->b_vflags &= ~BV_SCANNED; | bp->b_vflags &= ~BV_SCANNED; | ||||
TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) { | TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) { | ||||
/* | /* | ||||
* Reasons to skip this buffer: it has already been considered | * Reasons to skip this buffer: it has already been considered | ||||
* on this pass, the buffer has dependencies that will cause | * on this pass, the buffer has dependencies that will cause | ||||
* it to be redirtied and it has not already been deferred, | * it to be redirtied and it has not already been deferred, | ||||
* or it is already being written. | * or it is already being written. | ||||
*/ | */ | ||||
if ((bp->b_vflags & BV_SCANNED) != 0) | if ((bp->b_vflags & BV_SCANNED) != 0) | ||||
continue; | continue; | ||||
bp->b_vflags |= BV_SCANNED; | bp->b_vflags |= BV_SCANNED; | ||||
/* Flush indirects in order. */ | /* | ||||
* Flush indirects in order, if requested. | |||||
* | |||||
* Note that if only datasync is requested, we can | |||||
* skip indirect blocks when softupdates are not | |||||
* active. Otherwise we must flush them with data, | |||||
* since dependencies prevent data block writes. | |||||
*/ | |||||
if (waitfor == MNT_WAIT && bp->b_lblkno <= -NDADDR && | if (waitfor == MNT_WAIT && bp->b_lblkno <= -NDADDR && | ||||
lbn_level(bp->b_lblkno) >= passes) | (lbn_level(bp->b_lblkno) >= passes || | ||||
((flags & DATA_ONLY) != 0 && !DOINGSOFTDEP(vp)))) | |||||
continue; | continue; | ||||
if (bp->b_lblkno > lbn) | if (bp->b_lblkno > lbn) | ||||
panic("ffs_syncvnode: syncing truncated data."); | panic("ffs_syncvnode: syncing truncated data."); | ||||
if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL) == 0) { | if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL) == 0) { | ||||
BO_UNLOCK(bo); | BO_UNLOCK(bo); | ||||
} else if (wait != 0) { | } else if (wait) { | ||||
if (BUF_LOCK(bp, | if (BUF_LOCK(bp, | ||||
LK_EXCLUSIVE | LK_SLEEPFAIL | LK_INTERLOCK, | LK_EXCLUSIVE | LK_SLEEPFAIL | LK_INTERLOCK, | ||||
BO_LOCKPTR(bo)) != 0) { | BO_LOCKPTR(bo)) != 0) { | ||||
bp->b_vflags &= ~BV_SCANNED; | bp->b_vflags &= ~BV_SCANNED; | ||||
goto next; | goto next; | ||||
} | } | ||||
} else | } else | ||||
continue; | continue; | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | next: | ||||
* as a best effort. | * as a best effort. | ||||
* | * | ||||
* Regular files may need multiple passes to flush all dependency | * Regular files may need multiple passes to flush all dependency | ||||
* work as it is possible that we must write once per indirect | * work as it is possible that we must write once per indirect | ||||
* level, once for the leaf, and once for the inode and each of | * level, once for the leaf, and once for the inode and each of | ||||
* these will be done with one sync and one async pass. | * these will be done with one sync and one async pass. | ||||
*/ | */ | ||||
if (bo->bo_dirty.bv_cnt > 0) { | if (bo->bo_dirty.bv_cnt > 0) { | ||||
if ((flags & DATA_ONLY) != 0 && !DOINGSOFTDEP(vp)) { | |||||
/* | |||||
* For data-only sync, when not doing | |||||
* soft-updates, dirty indirect buffers are | |||||
* ignored. | |||||
*/ | |||||
still_dirty = false; | |||||
TAILQ_FOREACH(bp, &bo->bo_dirty.bv_hd, b_bobufs) { | |||||
if (bp->b_lblkno > -NDADDR) { | |||||
still_dirty = true; | |||||
break; | |||||
} | |||||
} | |||||
} else | |||||
still_dirty = true; | |||||
if (still_dirty) { | |||||
/* Write the inode after sync passes to flush deps. */ | /* Write the inode after sync passes to flush deps. */ | ||||
if (wait && DOINGSOFTDEP(vp) && (flags & NO_INO_UPDT) == 0) { | if (wait && DOINGSOFTDEP(vp) && | ||||
(flags & NO_INO_UPDT) == 0) { | |||||
BO_UNLOCK(bo); | BO_UNLOCK(bo); | ||||
ffs_update(vp, 1); | ffs_update(vp, 1); | ||||
BO_LOCK(bo); | BO_LOCK(bo); | ||||
} | } | ||||
/* switch between sync/async. */ | /* switch between sync/async. */ | ||||
wait = !wait; | wait = !wait; | ||||
if (wait == 1 || ++passes < NIADDR + 2) | if (wait || ++passes < NIADDR + 2) | ||||
goto loop; | goto loop; | ||||
#ifdef INVARIANTS | #ifdef INVARIANTS | ||||
if (!vn_isdisk(vp, NULL)) | if (!vn_isdisk(vp, NULL)) | ||||
vn_printf(vp, "ffs_fsync: dirty "); | vn_printf(vp, "ffs_fsync: dirty "); | ||||
#endif | #endif | ||||
} | } | ||||
} | |||||
BO_UNLOCK(bo); | BO_UNLOCK(bo); | ||||
error = 0; | error = 0; | ||||
if ((flags & DATA_ONLY) == 0) { | |||||
if ((flags & NO_INO_UPDT) == 0) | if ((flags & NO_INO_UPDT) == 0) | ||||
error = ffs_update(vp, 1); | error = ffs_update(vp, 1); | ||||
if (DOINGSUJ(vp)) | if (DOINGSUJ(vp)) | ||||
softdep_journal_fsync(VTOI(vp)); | softdep_journal_fsync(VTOI(vp)); | ||||
} | |||||
return (error); | return (error); | ||||
} | |||||
static int | |||||
ffs_fdatasync(struct vop_fdatasync_args *ap) | |||||
{ | |||||
return (ffs_syncvnode(ap->a_vp, MNT_WAIT, DATA_ONLY)); | |||||
} | } | ||||
static int | static int | ||||
ffs_lock(ap) | ffs_lock(ap) | ||||
struct vop_lock1_args /* { | struct vop_lock1_args /* { | ||||
struct vnode *a_vp; | struct vnode *a_vp; | ||||
int a_flags; | int a_flags; | ||||
struct thread *a_td; | struct thread *a_td; | ||||
▲ Show 20 Lines • Show All 1,384 Lines • Show Last 20 Lines |