no-copy-libafs-builds-20021015
[openafs.git] / src / afs / DUX / osi_vm.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include "afs/param.h"
12
13 RCSID("$Header$");
14
15 #include "afs/sysincludes.h"    /* Standard vendor system headers */
16 #include "afsincludes.h"        /* Afs-based standard headers */
17 #include "afs/afs_stats.h"  /* statistics */
18 #include <vm/vm_ubc.h>
19 #include <values.h>
20
21 /* Try to discard pages, in order to recycle a vcache entry.
22  *
23  * We also make some sanity checks:  ref count, open count, held locks.
24  *
25  * We also do some non-VM-related chores, such as releasing the cred pointer
26  * (for AIX and Solaris) and releasing the gnode (for AIX).
27  *
28  * Locking:  afs_xvcache lock is held.  If it is dropped and re-acquired,
29  *   *slept should be set to warn the caller.
30  *
31  * Formerly, afs_xvcache was dropped and re-acquired for Solaris, but now it
32  * is not dropped and re-acquired for any platform.  It may be that *slept is
33  * therefore obsolescent.
34  *
35  * OSF/1 Locking:  VN_LOCK has been called.
36  */
37 int osi_VM_FlushVCache(struct vcache *avc, int *slept)
38 {
39     if (avc->vrefCount > 1)
40         return EBUSY;
41
42     if (avc->opens)
43         return EBUSY;
44
45     /* if a lock is held, give up */
46     if (CheckLock(&avc->lock) || afs_CheckBozonLock(&avc->pvnLock))
47         return EBUSY;
48
49     AFS_GUNLOCK();
50     ubc_invalidate(AFSTOV(avc)->v_object, 0, 0, B_INVAL);
51     AFS_GLOCK();
52
53     return 0;
54 }
55
56 /*
57  * osi_ubc_flush_dirty_and_wait -- ensure all dirty pages cleaned
58  *
59  * Alpha OSF/1 doesn't make it easy to wait for all dirty pages to be cleaned.
60  * NFS tries to do this by calling waitforio(), which waits for v_numoutput
61  * to go to zero.  But that isn't good enough, because afs_putpage() doesn't
62  * increment v_numoutput until it has obtained the vcache entry lock.  Suppose
63  * that Process A, trying to flush a page, is waiting for that lock, and
64  * Process B tries to close the file.  Process B calls waitforio() which thinks
65  * that everything is cool because v_numoutput is still zero.  Process B then
66  * proceeds to call afs_StoreAllSegments().  Finally when B is finished, A gets
67  * to proceed and flush its page.  But then it's too late because the file is
68  * already closed.
69  *
70  * (I suspect that waitforio() is not adequate for NFS, just as it isn't
71  * adequate for us.  But that's not my problem.)
72  *
73  * The only way we can be sure that there are no more dirty pages is if there
74  * are no more pages with pg_busy set.  We look for them on the cleanpl.
75  *
76  * For some reason, ubc_flush_dirty() only looks at the dirtypl, not the
77  * dirtywpl.  I don't know why this is good enough, but I assume it is.  By
78  * the same token, I only look for busy pages on the cleanpl, not the cleanwpl.
79  *
80  * Called with the global lock NOT held.
81  */
82 static void osi_ubc_flush_dirty_and_wait(struct vnode *vp, int flags)
83 {
84     int retry;
85     vm_page_t pp;
86     int first;
87
88     do {
89         struct vm_ubc_object* vop;
90         vop = (struct vm_ubc_object*)(vp->v_object);
91         ubc_flush_dirty(vop, flags); 
92
93         vm_object_lock(vop);
94         if (vop->vu_dirtypl)
95             /* shouldn't happen, but who knows */
96             retry = 1;
97         else {
98             retry = 0;
99             if (vop->vu_cleanpl) {
100                 for (first = 1, pp = vop->vu_cleanpl;
101                      first || pp != vop->vu_cleanpl;
102                      first = 0, pp = pp->pg_onext) {
103                     if (pp->pg_busy) {
104                         retry = 1;
105                         pp->pg_wait = 1;
106                         assert_wait_mesg((vm_offset_t)pp, FALSE, "pg_wait");
107                         vm_object_unlock(vop);
108                         thread_block();
109                         break;
110                     }
111                 }
112             }
113             if (retry) continue;
114         }
115         vm_object_unlock(vop);
116     } while (retry);
117 }
118
119 /* Try to store pages to cache, in order to store a file back to the server.
120  *
121  * Locking:  the vcache entry's lock is held.  It will usually be dropped and
122  * re-obtained.
123  */
124 void osi_VM_StoreAllSegments(struct vcache *avc)
125 {
126     ReleaseWriteLock(&avc->lock);
127     AFS_GUNLOCK();
128     osi_ubc_flush_dirty_and_wait(AFSTOV(avc), 0);
129     AFS_GLOCK();
130     ObtainWriteLock(&avc->lock,94);
131 }
132
133 /* Try to invalidate pages, for "fs flush" or "fs flushv"; or
134  * try to free pages, when deleting a file.
135  *
136  * Locking:  the vcache entry's lock is held.  It may be dropped and 
137  * re-obtained.
138  *
139  * Since we drop and re-obtain the lock, we can't guarantee that there won't
140  * be some pages around when we return, newly created by concurrent activity.
141  */
142 void osi_VM_TryToSmush(struct vcache *avc, struct AFS_UCRED *acred, int sync)
143 {
144     ReleaseWriteLock(&avc->lock);
145     AFS_GUNLOCK();
146     osi_ubc_flush_dirty_and_wait(AFSTOV(avc), 0);
147     ubc_invalidate(AFSTOV(avc)->v_object, 0, 0, B_INVAL);
148     AFS_GLOCK();
149     ObtainWriteLock(&avc->lock,59);
150 }
151
152 /* Purge VM for a file when its callback is revoked.
153  *
154  * Locking:  No lock is held, not even the global lock.
155  */
156 void osi_VM_FlushPages(struct vcache *avc, struct AFS_UCRED *credp)
157 {
158     ubc_flush_dirty(AFSTOV(avc)->v_object, 0);
159     ubc_invalidate(AFSTOV(avc)->v_object, 0, 0, B_INVAL);
160 }
161
162 /* Purge pages beyond end-of-file, when truncating a file.
163  *
164  * Locking:  no lock is held, not even the global lock.
165  * activeV is raised.  This is supposed to block pageins, but at present
166  * it only works on Solaris.
167  */
168 void osi_VM_Truncate(struct vcache *avc, int alen, struct AFS_UCRED *acred)
169 {
170     ubc_invalidate(AFSTOV(avc)->v_object, alen,
171                         MAXINT - alen, B_INVAL);
172 }