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