SOLARIS: Avoid vcache locks when flushing pages for RO vnodes
[openafs.git] / src / afs / SOLARIS / osi_vnodeops.c
index f5e6ffd..9aa4f17 100644 (file)
@@ -463,6 +463,19 @@ afs_GetOnePage(struct vnode *vp, u_offset_t off, u_int alen, u_int *protp,
     return code;
 }
 
+/**
+ * Dummy pvn_vplist_dirty() handler for non-writable vnodes.
+ */
+static int
+afs_never_putapage(struct vnode *vp, struct page *pages, u_offset_t * offp,
+            size_t * lenp, int flags, afs_ucred_t *credp)
+{
+    struct vcache *avc = VTOAFS(vp);
+    osi_Assert((avc->f.states & CRO) != 0);
+    osi_Panic("Dirty pages while flushing a read-only volume vnode.");
+    return EIO; /* unreachable */
+}
+
 int
 afs_putpage(struct vnode *vp, offset_t off, u_int len, int flags, 
            afs_ucred_t *cred)
@@ -474,7 +487,7 @@ afs_putpage(struct vnode *vp, offset_t off, u_int len, int flags,
     afs_offs_t endPos;
     afs_int32 NPages = 0;
     u_offset_t toff = off;
-    int didWriteLock;
+    int didLock = 0;
 
     AFS_STATCNT(afs_putpage);
     if (vp->v_flag & VNOMAP)   /* file doesn't allow mapping */
@@ -488,11 +501,11 @@ afs_putpage(struct vnode *vp, offset_t off, u_int len, int flags,
               (afs_int32) vp, ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(off),
               ICL_TYPE_INT32, (afs_int32) len, ICL_TYPE_LONG, (int)flags);
     avc = VTOAFS(vp);
-    ObtainSharedLock(&avc->lock, 247);
-    didWriteLock = 0;
 
     /* Get a list of modified (or whatever) pages */
     if (len) {
+       ObtainSharedLock(&avc->lock, 247);
+       didLock = SHARED_LOCK;
        endPos = (afs_offs_t) off + len;        /* position we're supposed to write up to */
        while ((afs_offs_t) toff < endPos
               && (afs_offs_t) toff < avc->f.m.Length) {
@@ -507,9 +520,9 @@ afs_putpage(struct vnode *vp, offset_t off, u_int len, int flags,
            if (!pages || !pvn_getdirty(pages, flags))
                tlen = PAGESIZE;
            else {
-               if (!didWriteLock) {
+               if (didLock == SHARED_LOCK) {
                    AFS_GLOCK();
-                   didWriteLock = 1;
+                   didLock = WRITE_LOCK;
                    UpgradeSToWLock(&avc->lock, 671);
                    AFS_GUNLOCK();
                }
@@ -524,27 +537,50 @@ afs_putpage(struct vnode *vp, offset_t off, u_int len, int flags,
            AFS_GLOCK();
        }
     } else {
-       if (!didWriteLock) {
-           UpgradeSToWLock(&avc->lock, 670);
-           didWriteLock = 1;
+       /*
+        * We normally arrive here due to a vm flush.
+        *
+        * If this vnode belongs to a writable volume, obtain a vcache lock
+        * then call pvn_vplist_dirty to free, invalidate, or to write out
+        * dirty pages with afs_putapage.  The afs_putapage routine requires a
+        * vcache lock, so we obtain it here before any page locks are taken.
+        * This locking order is done to avoid deadlocking due to races with
+        * afs_getpage, which also takes vcache and page locks.
+        *
+        * If this vnode belongs to a non-writable volume, then it will not
+        * contain dirty pages, so we do not need to lock the vcache and since
+        * afs_putapage will not be called.  Instead, forgo the vcache lock and
+        * call pvn_vplist_dirty to free, or invalidate pages. Pass a dummy
+        * page out handler to pvn_vplist_dirty which we do not expect to be
+        * called.  Panic if the dummy handler is called, since something went
+        * horribly wrong.
+        */
+       if ((avc->f.states & CRO) == 0) {
+           ObtainWriteLock(&avc->lock, 670);
+           didLock = WRITE_LOCK;
        }
-
        AFS_GUNLOCK();
-       code = pvn_vplist_dirty(vp, toff, afs_putapage, flags, cred);
+       if ((avc->f.states & CRO) == 0)
+           code = pvn_vplist_dirty(vp, toff, afs_putapage, flags, cred);
+       else
+           code = pvn_vplist_dirty(vp, toff, afs_never_putapage, flags, cred);
        AFS_GLOCK();
     }
 
     if (code && !avc->vc_error) {
-       if (!didWriteLock) {
+       if (didLock == 0) {
+           ObtainWriteLock(&avc->lock, 668);
+           didLock = WRITE_LOCK;
+       } else if (didLock == SHARED_LOCK) {
            UpgradeSToWLock(&avc->lock, 669);
-           didWriteLock = 1;
+           didLock = WRITE_LOCK;
        }
        avc->vc_error = code;
     }
 
-    if (didWriteLock)
+    if (didLock == WRITE_LOCK)
        ReleaseWriteLock(&avc->lock);
-    else
+    else if (didLock == SHARED_LOCK)
        ReleaseSharedLock(&avc->lock);
     afs_Trace2(afs_iclSetp, CM_TRACE_PAGEOUTDONE, ICL_TYPE_LONG, code,
               ICL_TYPE_LONG, NPages);
@@ -987,7 +1023,7 @@ afs_map(struct vnode *vp, offset_t off, struct as *as, caddr_t *addr, size_t len
        (void)as_unmap(as, *addr, len); /* unmap old address space use */
     /* setup the create parameter block for the call */
     crargs.vp = AFSTOV(avc);
-    crargs.offset = (u_int) off;
+    crargs.offset = (u_offset_t)off;
     crargs.cred = cred;
     crargs.type = flags & MAP_TYPE;
     crargs.prot = prot;