Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/vfs_vnops.c
Show First 20 Lines • Show All 647 Lines • ▼ Show 20 Lines | if ((ioflg & IO_NODELOCKED) == 0) { | ||||
} else | } else | ||||
rl_cookie = NULL; | rl_cookie = NULL; | ||||
mp = NULL; | mp = NULL; | ||||
if (rw == UIO_WRITE) { | if (rw == UIO_WRITE) { | ||||
if (vp->v_type != VCHR && | if (vp->v_type != VCHR && | ||||
(error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) | (error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) | ||||
!= 0) | != 0) | ||||
goto out; | goto out; | ||||
if (MNT_SHARED_WRITES(mp) || | lock_flags = vn_lktype_write(mp, vp); | ||||
((mp == NULL) && MNT_SHARED_WRITES(vp->v_mount))) | |||||
lock_flags = LK_SHARED; | |||||
else | |||||
lock_flags = LK_EXCLUSIVE; | |||||
} else | } else | ||||
lock_flags = LK_SHARED; | lock_flags = LK_SHARED; | ||||
vn_lock(vp, lock_flags | LK_RETRY); | vn_lock(vp, lock_flags | LK_RETRY); | ||||
} else | } else | ||||
rl_cookie = NULL; | rl_cookie = NULL; | ||||
ASSERT_VOP_LOCKED(vp, "IO_NODELOCKED with no vp lock held"); | ASSERT_VOP_LOCKED(vp, "IO_NODELOCKED with no vp lock held"); | ||||
#ifdef MAC | #ifdef MAC | ||||
▲ Show 20 Lines • Show All 432 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
static int | static int | ||||
vn_write(struct file *fp, struct uio *uio, struct ucred *active_cred, int flags, | vn_write(struct file *fp, struct uio *uio, struct ucred *active_cred, int flags, | ||||
struct thread *td) | struct thread *td) | ||||
{ | { | ||||
struct vnode *vp; | struct vnode *vp; | ||||
struct mount *mp; | struct mount *mp; | ||||
off_t orig_offset; | off_t orig_offset; | ||||
int error, ioflag, lock_flags; | int error, ioflag; | ||||
int advice; | int advice; | ||||
bool need_finished_write; | bool need_finished_write; | ||||
KASSERT(uio->uio_td == td, ("uio_td %p is not td %p", | KASSERT(uio->uio_td == td, ("uio_td %p is not td %p", | ||||
uio->uio_td, td)); | uio->uio_td, td)); | ||||
KASSERT(flags & FOF_OFFSET, ("No FOF_OFFSET")); | KASSERT(flags & FOF_OFFSET, ("No FOF_OFFSET")); | ||||
vp = fp->f_vnode; | vp = fp->f_vnode; | ||||
if (vp->v_type == VREG) | if (vp->v_type == VREG) | ||||
Show All 24 Lines | if (vp->v_type != VCHR) { | ||||
error = vn_start_write(vp, &mp, V_WAIT | PCATCH); | error = vn_start_write(vp, &mp, V_WAIT | PCATCH); | ||||
if (error != 0) | if (error != 0) | ||||
goto unlock; | goto unlock; | ||||
need_finished_write = true; | need_finished_write = true; | ||||
} | } | ||||
advice = get_advice(fp, uio); | advice = get_advice(fp, uio); | ||||
if (MNT_SHARED_WRITES(mp) || | vn_lock(vp, vn_lktype_write(mp, vp) | LK_RETRY); | ||||
(mp == NULL && MNT_SHARED_WRITES(vp->v_mount))) { | |||||
lock_flags = LK_SHARED; | |||||
} else { | |||||
lock_flags = LK_EXCLUSIVE; | |||||
} | |||||
vn_lock(vp, lock_flags | LK_RETRY); | |||||
switch (advice) { | switch (advice) { | ||||
case POSIX_FADV_NORMAL: | case POSIX_FADV_NORMAL: | ||||
case POSIX_FADV_SEQUENTIAL: | case POSIX_FADV_SEQUENTIAL: | ||||
case POSIX_FADV_NOREUSE: | case POSIX_FADV_NOREUSE: | ||||
ioflag |= sequential_heuristic(uio, fp); | ioflag |= sequential_heuristic(uio, fp); | ||||
break; | break; | ||||
case POSIX_FADV_RANDOM: | case POSIX_FADV_RANDOM: | ||||
/* XXX: Is this correct? */ | /* XXX: Is this correct? */ | ||||
▲ Show 20 Lines • Show All 1,889 Lines • ▼ Show 20 Lines | |||||
* already in the output file. | * already in the output file. | ||||
*/ | */ | ||||
static int | static int | ||||
vn_write_outvp(struct vnode *outvp, char *dat, off_t outoff, off_t xfer, | vn_write_outvp(struct vnode *outvp, char *dat, off_t outoff, off_t xfer, | ||||
u_long blksize, bool growfile, bool checkhole, struct ucred *cred) | u_long blksize, bool growfile, bool checkhole, struct ucred *cred) | ||||
{ | { | ||||
struct mount *mp; | struct mount *mp; | ||||
off_t dataoff, holeoff, xfer2; | off_t dataoff, holeoff, xfer2; | ||||
int error, lckf; | int error; | ||||
/* | /* | ||||
* Loop around doing writes of blksize until write has been completed. | * Loop around doing writes of blksize until write has been completed. | ||||
* Lock/unlock on each loop iteration so that a bwillwrite() can be | * Lock/unlock on each loop iteration so that a bwillwrite() can be | ||||
* done for each iteration, since the xfer argument can be very | * done for each iteration, since the xfer argument can be very | ||||
* large if there is a large hole to punch in the output file. | * large if there is a large hole to punch in the output file. | ||||
*/ | */ | ||||
error = 0; | error = 0; | ||||
Show All 22 Lines | do { | ||||
if (growfile) { | if (growfile) { | ||||
error = vn_lock(outvp, LK_EXCLUSIVE); | error = vn_lock(outvp, LK_EXCLUSIVE); | ||||
if (error == 0) { | if (error == 0) { | ||||
error = vn_truncate_locked(outvp, outoff + xfer, | error = vn_truncate_locked(outvp, outoff + xfer, | ||||
false, cred); | false, cred); | ||||
VOP_UNLOCK(outvp); | VOP_UNLOCK(outvp); | ||||
} | } | ||||
} else { | } else { | ||||
if (MNT_SHARED_WRITES(mp)) | error = vn_lock(outvp, vn_lktype_write(mp, outvp)); | ||||
markj: When is it possible for vn_start_write() to return `mp = NULL`? Clearly it will happen if the… | |||||
Done Inline ActionsOfficial case is for VOP_GETWRITEMOUNT() to return EOPNOTSUPP. In this case vn_start_write() succeeds and sets mp to NULL, regardless of the reclamation status of the vnode. This was designed as a simple opt-out for filesystems that do need support write suspension and do not want to pay for the overhead of maintaining the mnt_writeopcount. Until we have such support, we should accept a possibility that mp == NULL but vp->v_mount != NULL. kib: Official case is for VOP_GETWRITEMOUNT() to return EOPNOTSUPP. In this case vn_start_write()… | |||||
lckf = LK_SHARED; | |||||
else | |||||
lckf = LK_EXCLUSIVE; | |||||
error = vn_lock(outvp, lckf); | |||||
if (error == 0) { | if (error == 0) { | ||||
error = vn_rdwr(UIO_WRITE, outvp, dat, xfer2, | error = vn_rdwr(UIO_WRITE, outvp, dat, xfer2, | ||||
outoff, UIO_SYSSPACE, IO_NODELOCKED, | outoff, UIO_SYSSPACE, IO_NODELOCKED, | ||||
curthread->td_ucred, cred, NULL, curthread); | curthread->td_ucred, cred, NULL, curthread); | ||||
outoff += xfer2; | outoff += xfer2; | ||||
xfer -= xfer2; | xfer -= xfer2; | ||||
VOP_UNLOCK(outvp); | VOP_UNLOCK(outvp); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 411 Lines • ▼ Show 20 Lines | if (vp2_locked && vp1 != NULL) { | ||||
vn_lock(vp1, LK_EXCLUSIVE | LK_RETRY); | vn_lock(vp1, LK_EXCLUSIVE | LK_RETRY); | ||||
vp1_locked = true; | vp1_locked = true; | ||||
} | } | ||||
} | } | ||||
if (vp1 != NULL) | if (vp1 != NULL) | ||||
ASSERT_VOP_ELOCKED(vp1, "vp1 ret"); | ASSERT_VOP_ELOCKED(vp1, "vp1 ret"); | ||||
if (vp2 != NULL) | if (vp2 != NULL) | ||||
ASSERT_VOP_ELOCKED(vp2, "vp2 ret"); | ASSERT_VOP_ELOCKED(vp2, "vp2 ret"); | ||||
} | |||||
int | |||||
vn_lktype_write(struct mount *mp, struct vnode *vp) | |||||
{ | |||||
if (MNT_SHARED_WRITES(mp) || | |||||
(mp == NULL && MNT_SHARED_WRITES(vp->v_mount))) | |||||
return (LK_SHARED); | |||||
return (LK_EXCLUSIVE); | |||||
} | } |
When is it possible for vn_start_write() to return mp = NULL? Clearly it will happen if the vnode is doomed. Are there other cases? If so, is it possible to have mp == NULL && outvp->v_mount != NULL?