From 04aa807cb6d18828a8121143063cc9ec2120bd79 Mon Sep 17 00:00:00 2001 From: Tor Egge Date: Mon, 2 Oct 2006 02:06:27 +0000 Subject: [PATCH] If the buffer lock has waiters after the buffer has changed identity then getnewbuf() needs to drop the buffer in order to wake waiters that might sleep on the buffer in the context of the old identity. --- sys/kern/kern_lock.c | 15 +++++++++++++++ sys/kern/vfs_bio.c | 11 +++++++++++ sys/sys/buf.h | 11 +++++++++++ sys/sys/lockmgr.h | 1 + 4 files changed, 38 insertions(+) diff --git a/sys/kern/kern_lock.c b/sys/kern/kern_lock.c index c0b929ab8790..32b43b863b2d 100644 --- a/sys/kern/kern_lock.c +++ b/sys/kern/kern_lock.c @@ -565,6 +565,21 @@ lockcount(lkp) return (count); } +/* + * Determine the number of waiters on a lock. + */ +int +lockwaiters(lkp) + struct lock *lkp; +{ + int count; + + mtx_lock(lkp->lk_interlock); + count = lkp->lk_waitcount; + mtx_unlock(lkp->lk_interlock); + return (count); +} + /* * Print out information about state of a lock. Used by VOP_PRINT * routines to display status about contained locks. diff --git a/sys/kern/vfs_bio.c b/sys/kern/vfs_bio.c index 37ef55213a1e..904e84c1803c 100644 --- a/sys/kern/vfs_bio.c +++ b/sys/kern/vfs_bio.c @@ -1889,6 +1889,17 @@ getnewbuf(int slpflag, int slptimeo, int size, int maxsize) goto restart; } + /* + * Notify any waiters for the buffer lock about + * identity change by freeing the buffer. + */ + if (qindex == QUEUE_CLEAN && BUF_LOCKWAITERS(bp) > 0) { + bp->b_flags |= B_INVAL; + bfreekva(bp); + brelse(bp); + goto restart; + } + /* * If we are overcomitted then recover the buffer and its * KVM space. This occurs in rare situations when multiple diff --git a/sys/sys/buf.h b/sys/sys/buf.h index afa3e232195b..359be84af143 100644 --- a/sys/sys/buf.h +++ b/sys/sys/buf.h @@ -371,6 +371,17 @@ BUF_REFCNT(struct buf *bp) return ret; } + +/* + * Find out the number of waiters on a lock. + */ +static __inline int BUF_LOCKWAITERS(struct buf *); +static __inline int +BUF_LOCKWAITERS(struct buf *bp) +{ + return (lockwaiters(&bp->b_lock)); +} + #endif /* _KERNEL */ struct buf_queue_head { diff --git a/sys/sys/lockmgr.h b/sys/sys/lockmgr.h index dd0eed9b15a5..9c9b59bc8adc 100644 --- a/sys/sys/lockmgr.h +++ b/sys/sys/lockmgr.h @@ -203,6 +203,7 @@ void transferlockers(struct lock *, struct lock *); void lockmgr_printinfo(struct lock *); int lockstatus(struct lock *, struct thread *); int lockcount(struct lock *); +int lockwaiters(struct lock *); #ifdef DDB int lockmgr_chain(struct thread *td, struct thread **ownerp); #endif