+/*
+ * Update outstanding I/O count and do wakeup if requested.
+ */
+vwakeup(bp)
+ register struct buf *bp;
+{
+ register struct vnode *vp;
+
+ bp->b_dirtyoff = bp->b_dirtyend = 0;
+ if (vp = bp->b_vp) {
+ vp->v_numoutput--;
+ if ((vp->v_flag & VBWAIT) && vp->v_numoutput <= 0) {
+ if (vp->v_numoutput < 0)
+ panic("vwakeup: neg numoutput");
+ vp->v_flag &= ~VBWAIT;
+ wakeup((caddr_t)&vp->v_numoutput);
+ }
+ }
+}
+
+/*
+ * Invalidate in core blocks belonging to closed or umounted filesystem
+ *
+ * Go through the list of vnodes associated with the file system;
+ * for each vnode invalidate any buffers that it holds. Normally
+ * this routine is preceeded by a bflush call, so that on a quiescent
+ * filesystem there will be no dirty buffers when we are done. Binval
+ * returns the count of dirty buffers when it is finished.
+ */
+mntinvalbuf(mountp)
+ struct mount *mountp;
+{
+ register struct vnode *vp;
+ int dirty = 0;
+
+ if ((mountp->mnt_flag & MNT_MPBUSY) == 0)
+ panic("mntinvalbuf: not busy");
+loop:
+ for (vp = mountp->mnt_mounth; vp; vp = vp->v_mountf) {
+ if (vget(vp))
+ goto loop;
+ dirty += vinvalbuf(vp, 1);
+ vput(vp);
+ if (vp->v_mount != mountp)
+ goto loop;
+ }
+ return (dirty);
+}
+
+/*
+ * Flush out and invalidate all buffers associated with a vnode.
+ * Called with the underlying object locked.
+ */
+vinvalbuf(vp, save)
+ register struct vnode *vp;
+ int save;
+{
+ register struct buf *bp;
+ struct buf *nbp, *blist;
+ int s, dirty = 0;
+
+ for (;;) {
+ if (blist = vp->v_dirtyblkhd)
+ /* void */;
+ else if (blist = vp->v_cleanblkhd)
+ /* void */;
+ else
+ break;
+ for (bp = blist; bp; bp = nbp) {
+ nbp = bp->b_blockf;
+ s = splbio();
+ if (bp->b_flags & B_BUSY) {
+ bp->b_flags |= B_WANTED;
+ sleep((caddr_t)bp, PRIBIO + 1);
+ splx(s);
+ break;
+ }
+ bremfree(bp);
+ bp->b_flags |= B_BUSY;
+ splx(s);
+ if (save && (bp->b_flags & B_DELWRI)) {
+ dirty++;
+ (void) bwrite(bp);
+ break;
+ }
+ if (bp->b_vp != vp)
+ reassignbuf(bp, bp->b_vp);
+ else
+ bp->b_flags |= B_INVAL;
+ brelse(bp);
+ }
+ }
+ if (vp->v_dirtyblkhd || vp->v_cleanblkhd)
+ panic("vinvalbuf: flush failed");
+ return (dirty);
+}
+
+/*
+ * Associate a buffer with a vnode.
+ */
+bgetvp(vp, bp)
+ register struct vnode *vp;
+ register struct buf *bp;
+{
+ register struct vnode *vq;
+ register struct buf *bq;
+
+ if (bp->b_vp)
+ panic("bgetvp: not free");
+ VHOLD(vp);
+ bp->b_vp = vp;
+ if (vp->v_type == VBLK || vp->v_type == VCHR)
+ bp->b_dev = vp->v_rdev;
+ else
+ bp->b_dev = NODEV;
+ /*
+ * Insert onto list for new vnode.
+ */
+ if (bq = vp->v_cleanblkhd)
+ bq->b_blockb = &bp->b_blockf;
+ bp->b_blockf = bq;
+ bp->b_blockb = &vp->v_cleanblkhd;
+ vp->v_cleanblkhd = bp;
+}
+
+/*
+ * Disassociate a buffer from a vnode.
+ */
+brelvp(bp)
+ register struct buf *bp;
+{
+ struct buf *bq;
+ struct vnode *vp;
+
+ if (bp->b_vp == (struct vnode *) 0)
+ panic("brelvp: NULL");
+ /*
+ * Delete from old vnode list, if on one.
+ */
+ if (bp->b_blockb) {
+ if (bq = bp->b_blockf)
+ bq->b_blockb = bp->b_blockb;
+ *bp->b_blockb = bq;
+ bp->b_blockf = NULL;
+ bp->b_blockb = NULL;
+ }
+ vp = bp->b_vp;
+ bp->b_vp = (struct vnode *) 0;
+ HOLDRELE(vp);
+}
+
+/*
+ * Reassign a buffer from one vnode to another.
+ * Used to assign file specific control information
+ * (indirect blocks) to the vnode to which they belong.
+ */
+reassignbuf(bp, newvp)
+ register struct buf *bp;
+ register struct vnode *newvp;
+{
+ register struct buf *bq, **listheadp;
+
+ if (newvp == NULL)
+ panic("reassignbuf: NULL");
+ /*
+ * Delete from old vnode list, if on one.
+ */
+ if (bp->b_blockb) {
+ if (bq = bp->b_blockf)
+ bq->b_blockb = bp->b_blockb;
+ *bp->b_blockb = bq;
+ }
+ /*
+ * If dirty, put on list of dirty buffers;
+ * otherwise insert onto list of clean buffers.
+ */
+ if (bp->b_flags & B_DELWRI)
+ listheadp = &newvp->v_dirtyblkhd;
+ else
+ listheadp = &newvp->v_cleanblkhd;
+ if (bq = *listheadp)
+ bq->b_blockb = &bp->b_blockf;
+ bp->b_blockf = bq;
+ bp->b_blockb = listheadp;
+ *listheadp = bp;
+}
+