diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c --- a/sys/kern/vfs_vnops.c +++ b/sys/kern/vfs_vnops.c @@ -751,8 +751,8 @@ } #if OFF_MAX <= LONG_MAX -off_t -foffset_lock(struct file *fp, int flags) +static int +foffset_lock1(struct file *fp, int flags, off_t *offp) { volatile short *flagsp; off_t res; @@ -760,16 +760,22 @@ KASSERT((flags & FOF_OFFSET) == 0, ("FOF_OFFSET passed")); - if ((flags & FOF_NOLOCK) != 0) - return (atomic_load_long(&fp->f_offset)); + if ((flags & FOF_NOLOCK) != 0) { + *offp = atomic_load_long(&fp->f_offset); + return (0); + } /* * According to McKusick the vn lock was protecting f_offset here. * It is now protected by the FOFFSET_LOCKED flag. */ flagsp = &fp->f_vnread_flags; - if (atomic_cmpset_acq_16(flagsp, 0, FOFFSET_LOCKED)) - return (atomic_load_long(&fp->f_offset)); + if (atomic_cmpset_acq_16(flagsp, 0, FOFFSET_LOCKED)) { + *offp = atomic_load_long(&fp->f_offset); + return (0); + } + if ((flags & FOF_NONBLOCK) != 0) + return (EAGAIN); sleepq_lock(&fp->f_vnread_flags); state = atomic_load_16(flagsp); @@ -794,7 +800,8 @@ } res = atomic_load_long(&fp->f_offset); sleepq_release(&fp->f_vnread_flags); - return (res); + *offp = res; + return (0); } void @@ -836,8 +843,8 @@ return (atomic_load_long(&fp->f_offset)); } #else -off_t -foffset_lock(struct file *fp, int flags) +int +foffset_lock1(struct file *fp, int flags, off_t *offp) { struct mtx *mtxp; off_t res; @@ -848,6 +855,10 @@ mtx_lock(mtxp); if ((flags & FOF_NOLOCK) == 0) { while (fp->f_vnread_flags & FOFFSET_LOCKED) { + if ((flags & FOF_NONBLOCK) != 0) { + mtx_unlock(mtxp); + return (EAGAIN); + } fp->f_vnread_flags |= FOFFSET_LOCK_WAITING; msleep(&fp->f_vnread_flags, mtxp, PUSER -1, "vofflock", 0); @@ -856,7 +867,8 @@ } res = fp->f_offset; mtx_unlock(mtxp); - return (res); + *offp = res; + return (0); } void @@ -892,6 +904,20 @@ } #endif +off_t +foffset_lock(struct file *fp, int flags) +{ + off_t ret; + int error __diagused; + + KASSERT((flags & FOF_NONBLOCK) == 0, + ("FOF_NONBLOCK passed to foffset_lock()")); + + error = foffset_lock1(fp, flags, &ret); + KASSERT(error == 0, ("foffset_lock1() failed: %d", error)); + return (ret); +} + void foffset_lock_uio(struct file *fp, struct uio *uio, int flags) { @@ -900,6 +926,13 @@ uio->uio_offset = foffset_lock(fp, flags); } +int +foffset_trylock(struct file *fp, int flags, off_t *offp) +{ + + return (foffset_lock1(fp, flags | FOF_NONBLOCK, offp)); +} + void foffset_unlock_uio(struct file *fp, struct uio *uio, int flags) { diff --git a/sys/sys/file.h b/sys/sys/file.h --- a/sys/sys/file.h +++ b/sys/sys/file.h @@ -85,7 +85,9 @@ #define FOF_NEXTOFF_R 0x04 /* Also update f_nextoff[UIO_READ] */ #define FOF_NEXTOFF_W 0x08 /* Also update f_nextoff[UIO_WRITE] */ #define FOF_NOUPDATE 0x10 /* Do not update f_offset */ +#define FOF_NONBLOCK 0x20 /* Do not block if the offset is locked */ off_t foffset_lock(struct file *fp, int flags); +int foffset_trylock(struct file *fp, int flags, off_t *offp); void foffset_lock_uio(struct file *fp, struct uio *uio, int flags); void foffset_unlock(struct file *fp, off_t val, int flags); void foffset_unlock_uio(struct file *fp, struct uio *uio, int flags);