e9628f23fcefe96d4911489990f4863be517af5e
[openafs.git] / src / afs / afs_cbqueue.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 /*
11  * This package is used to actively manage the expiration of callbacks,
12  * so that the rest of the cache manager doesn't need to compute
13  * whether a callback has expired or not, but can tell with one simple
14  * check, that is, whether the CStatd bit is on or off.
15  *
16  * The base of the hash table moves periodically (every 128 seconds)
17  * QueueCallback rarely touches the first 3 slots in the hash table
18  * (only when called from CheckCallbacks) since MinTimeOut in
19  * viced/callback.c is currently 7 minutes. 
20  * Therefore, CheckCallbacks should be able to run concurrently with
21  * QueueCallback, given the proper locking, of course.
22  *
23  * Note:
24  * 1. CheckCallbacks and BumpBase never run simultaneously.  This is because
25  * they are only called from afs_Daemon.  Therefore, base and basetime will 
26  * always be consistent during CheckCallbacks.
27  * 2. cbHashT [base] rarely (if ever) gets stuff queued in it.  The only way 
28  * that could happen is CheckCallbacks might fencepost and move something in
29  * place, or BumpBase might push some stuff up.
30  * 3. Hash chains aren't particularly sorted. 
31  * 4. The file server keeps its callback state around for 3 minutes
32  * longer than it promises the cache manager in order to account for
33  * clock skew, network delay, and other bogeymen.
34  *
35  * For now I just use one large lock, which is fine on a uniprocessor,
36  * since it's not held during any RPCs or low-priority I/O operations. 
37  * To make this code MP-fast, you need no more locks than processors, 
38  * but probably more than one.  In measurements on MP-safe implementations, 
39  * I have never seen any contention over the xcbhash lock.
40  *
41  * Incompatible operations:
42  * Enqueue and "dequeue of first vcache" in same slot
43  * dequeue and "dequeue of preceding vcache" in same slot
44  * dequeue and "dequeue of successive vcache" in same slot
45  * BumpBase pushing a list and enqueue in the new base slot
46  * Two enqueues in same slot
47  * more...
48  *
49  * Certain invariants exist:
50  *    1  Callback expiration times granted by a file server will never
51  *       decrease for a particular vnode UNLESS a CallBack RPC is invoked
52  *       by the server in the interim.  
53  *    2  A vcache will always expire no sooner than the slot in which it is
54  *       currently enqueued.  Callback times granted by the server may 
55  *       increase, in which case the vcache will be updated in-place.  As a 
56  *       result, it may expire later than the slot in which it is enqueued.  
57  *       Not to worry, the CheckCallbacks code will move it if neccessary.
58  *       This approach means that busy vnodes won't be continually moved 
59  *       around within the expiry queue: they are only moved when they
60  *       finally advance to the lead bucket.
61  *    3  Anything which has a callback on it must be in the expiry
62  *       queue.  In AFS 3.3, that means everything but symlinks (which
63  *       are immutable), including contents of Read-Only volumes
64  *       (which have callbacks by virtue of the whole-volume callback)
65  *
66  * QueueCallback only checks that its vcache is in the list
67  * somewhere, counting on invariant #1 to guarantee that the vcache
68  * won't be in a slot later than QueueCallback would otherwise place
69  * it. Therefore, whenever we turn off the CStatd bit on the vcache, we
70  * *must* remove the vcache from the expiry queue.  Otherwise, we
71  * might have missed a CallBack RPC, and a subsequent callback might be
72  * granted with a shorter expiration time.
73  */
74 #include <afsconfig.h>
75 #include "afs/param.h"
76
77 RCSID
78     ("$Header$");
79
80 #include "afs/sysincludes.h"    /*Standard vendor system headers */
81 #include "afsincludes.h"        /*AFS-based standard headers */
82 #include "afs/afs_cbqueue.h"
83 #include "afs/afs.h"
84 #include "afs/lock.h"
85 #include "afs/afs_stats.h"
86
87 static unsigned int base = 0;
88 static unsigned int basetime = 0;
89 static struct vcache *debugvc;  /* used only for post-mortem debugging */
90 struct bucket {
91     struct afs_q head;
92     /*  struct afs_lock lock;  only if you want lots of locks... */
93 };
94 static struct bucket cbHashT[CBHTSIZE];
95 struct afs_lock afs_xcbhash;
96
97 /* afs_QueueCallback
98  * Takes a write-locked vcache pointer and a callback expiration time
99  * as returned by the file server (ie, in units of 128 seconds from "now").
100  * 
101  * Uses the time as an index into a hash table, and inserts the vcache
102  * structure into the overflow chain.
103  * 
104  * If the vcache is already on some hash chain, leave it there.
105  * CheckCallbacks will get to it eventually.  In the meantime, it
106  * might get flushed, or it might already be on the right hash chain, 
107  * so why bother messing with it now?
108  *
109  * NOTE: The caller must hold a write lock on afs_xcbhash
110  */
111
112 void
113 afs_QueueCallback(struct vcache *avc, unsigned int atime, struct volume *avp)
114 {
115     if (avp && (avp->expireTime < avc->cbExpires))
116         avp->expireTime = avc->cbExpires;
117     if (!(avc->callsort.next)) {
118         atime = (atime + base) % CBHTSIZE;
119         QAdd(&(cbHashT[atime].head), &(avc->callsort));
120     }
121
122     return;
123 }                               /* afs_QueueCallback */
124
125 /* afs_DequeueCallback
126  * Takes a write-locked vcache pointer and removes it from the callback
127  * hash table, without knowing beforehand which slot it was in.
128  *
129  * for now, just get a lock on everything when doing the dequeue, don't
130  * worry about getting a lock on the individual slot.
131  * 
132  * the only other places that do anything like dequeues are CheckCallbacks
133  * and BumpBase.
134  *
135  * NOTE: The caller must hold a write lock on afs_xcbhash
136  */
137 void
138 afs_DequeueCallback(struct vcache *avc)
139 {
140
141     debugvc = avc;
142     if (avc->callsort.prev) {
143         QRemove(&(avc->callsort));
144     } else;                     /* must have got dequeued in a race */
145
146     return;
147 }                               /* afs_DequeueCallback */
148
149 /* afs_CheckCallbacks
150  * called periodically to determine which callbacks are likely to
151  * expire in the next n second interval.  Preemptively marks them as
152  * expired.  Rehashes items which are now in the wrong hash bucket.
153  * Preemptively renew recently-accessed items.  Only removes things
154  * from the first and second bucket (as long as secs < 128), and
155  * inserts things into other, later buckets.  either need to advance
156  * to the second bucket if secs spans two intervals, or else be
157  * certain to call afs_CheckCallbacks immediately after calling
158  * BumpBase (allows a little more slop but it's ok because file server
159  * keeps 3 minutes of slop time)
160  *
161  * There is a little race between CheckCallbacks and any code which
162  * updates cbExpires, always just prior to calling QueueCallback. We
163  * don't lock the vcache struct here (can't, or we'd risk deadlock),
164  * so GetVCache (for example) may update cbExpires before or after #1
165  * below.  If before, CheckCallbacks moves this entry to its proper
166  * slot.  If after, GetVCache blocks in the call to QueueCallbacks,
167  * this code dequeues the vcache, and then QueueCallbacks re-enqueues it. 
168  *
169  * XXX to avoid the race, make QueueCallback take the "real" time
170  * and update cbExpires under the xcbhash lock. 
171  *
172  * NB #1: There's a little optimization here: if I go to invalidate a
173  * RO vcache or volume, first check to see if the server is down.  If
174  * it _is_, don't invalidate it, cuz we might just as well keep using
175  * it.  Possibly, we could do the same thing for items in RW volumes,
176  * but that bears some drinking about.
177  *
178  * Don't really need to invalidate the hints, we could just wait to see if
179  * the dv has changed after a subsequent FetchStatus, but this is safer.
180  */
181
182 /* Sanity check on the callback queue. Allow for slop in the computation. */
183 #ifdef AFS_OSF_ENV
184 #define CBQ_LIMIT (afs_maxvcount + 10)
185 #else
186 #define CBQ_LIMIT (afs_cacheStats + afs_stats_cmperf.vcacheXAllocs + 10)
187 #endif
188
189 void
190 afs_CheckCallbacks(unsigned int secs)
191 {
192     struct vcache *tvc;
193     register struct afs_q *tq;
194     struct afs_q *uq;
195     afs_uint32 now;
196     struct volume *tvp;
197     register int safety;
198
199     ObtainWriteLock(&afs_xcbhash, 85);  /* pretty likely I'm going to remove something */
200     now = osi_Time();
201     for (safety = 0, tq = cbHashT[base].head.prev;
202          (safety <= CBQ_LIMIT) && (tq != &(cbHashT[base].head));
203          tq = uq, safety++) {
204
205         uq = QPrev(tq);
206         tvc = CBQTOV(tq);
207         if (tvc->cbExpires < now + secs) {      /* race #1 here */
208             /* Get the volume, and if its callback expiration time is more than secs
209              * seconds into the future, update this vcache entry and requeue it below
210              */
211             if ((tvc->f.states & CRO)
212                 && (tvp = afs_FindVolume(&(tvc->f.fid), READ_LOCK))) {
213                 if (tvp->expireTime > now + secs) {
214                     tvc->cbExpires = tvp->expireTime;   /* XXX race here */
215                 } else {
216                     int i;
217                     for (i = 0; i < MAXHOSTS && tvp->serverHost[i]; i++) {
218                         if (!(tvp->serverHost[i]->flags & SRVR_ISDOWN)) {
219                             /* What about locking xvcache or vrefcount++ or
220                              * write locking tvc? */
221                             QRemove(tq);
222                             tvc->f.states &= ~(CStatd | CMValid | CUnique);
223                             if (!(tvc->f.states & (CVInit|CVFlushed)) &&
224                                 (tvc->f.fid.Fid.Vnode & 1 ||
225                                  (vType(tvc) == VDIR)))
226                                 osi_dnlc_purgedp(tvc);
227                             tvc->dchint = NULL; /*invalidate em */
228                             afs_ResetVolumeInfo(tvp);
229                             break;
230                         }
231                     }
232                 }
233                 afs_PutVolume(tvp, READ_LOCK);
234             } else {
235                 /* Do I need to worry about things like execsorwriters?
236                  * What about locking xvcache or vrefcount++ or write locking tvc?
237                  */
238                 QRemove(tq);
239                 tvc->f.states &= ~(CStatd | CMValid | CUnique);
240                 if (!(tvc->f.states & (CVInit|CVFlushed)) &&
241                     (tvc->f.fid.Fid.Vnode & 1 || (vType(tvc) == VDIR)))
242                     osi_dnlc_purgedp(tvc);
243             }
244         }
245
246         if ((tvc->cbExpires > basetime) && CBHash(tvc->cbExpires - basetime)) {
247             /* it's been renewed on us.  Have to be careful not to put it back
248              * into this slot, or we may never get out of here.
249              */
250             int slot;
251             slot = (CBHash(tvc->cbExpires - basetime) + base) % CBHTSIZE;
252             if (slot != base) {
253                 if (QPrev(tq))
254                     QRemove(&(tvc->callsort));
255                 QAdd(&(cbHashT[slot].head), &(tvc->callsort));
256                 /* XXX remember to update volume expiration time */
257                 /* -- not needed for correctness, though */
258             }
259         }
260     }
261
262     if (safety > CBQ_LIMIT) {
263         afs_stats_cmperf.cbloops++;
264         if (afs_paniconwarn)
265             osi_Panic("CheckCallbacks");
266
267         afs_warn
268             ("AFS Internal Error (minor): please contact AFS Product Support.\n");
269         ReleaseWriteLock(&afs_xcbhash);
270         afs_FlushCBs();
271         return;
272     } else
273         ReleaseWriteLock(&afs_xcbhash);
274
275
276 /* XXX future optimization:
277    if this item has been recently accessed, queue up a stat for it.
278    {
279    struct dcache * adc;
280
281    ObtainReadLock(&afs_xdcache);
282    if ((adc = tvc->quick.dc) && (adc->stamp == tvc->quick.stamp)
283    && (afs_indexTimes[adc->index] > afs_indexCounter - 20)) {
284    queue up the stat request
285    }
286    ReleaseReadLock(&afs_xdcache);
287    }
288    */
289
290     return;
291 }                               /* afs_CheckCallback */
292
293 /* afs_FlushCBs 
294  * to be used only in dire circumstances, this drops all callbacks on
295  * the floor, without giving them back to the server.  It's ok, the server can 
296  * deal with it, but it is a little bit rude.
297  */
298 void
299 afs_FlushCBs(void)
300 {
301     register int i;
302     register struct vcache *tvc;
303
304     ObtainWriteLock(&afs_xcbhash, 86);  /* pretty likely I'm going to remove something */
305
306     for (i = 0; i < VCSIZE; i++)        /* reset all the vnodes */
307         for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
308             tvc->callback = 0;
309             tvc->dchint = NULL; /* invalidate hints */
310             tvc->f.states &= ~(CStatd);
311             if (QPrev(&(tvc->callsort)))
312                 QRemove(&(tvc->callsort));
313             if (!(tvc->f.states & (CVInit|CVFlushed)) &&
314                 ((tvc->f.fid.Fid.Vnode & 1) || (vType(tvc) == VDIR)))
315                 osi_dnlc_purgedp(tvc);
316         }
317
318     afs_InitCBQueue(0);
319
320     ReleaseWriteLock(&afs_xcbhash);
321 }
322
323 /* afs_FlushServerCBs
324  * to be used only in dire circumstances, this drops all callbacks on
325  * the floor for a specific server, without giving them back to the server.
326  * It's ok, the server can deal with it, but it is a little bit rude.
327  */
328 void
329 afs_FlushServerCBs(struct server *srvp)
330 {
331     register int i;
332     register struct vcache *tvc;
333
334     ObtainWriteLock(&afs_xcbhash, 86);  /* pretty likely I'm going to remove something */
335
336     for (i = 0; i < VCSIZE; i++) {      /* reset all the vnodes */
337         for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
338             if (tvc->callback == srvp) {
339                 tvc->callback = 0;
340                 tvc->dchint = NULL;     /* invalidate hints */
341                 tvc->f.states &= ~(CStatd);
342                 if (!(tvc->f.states & (CVInit|CVFlushed)) &&
343                     ((tvc->f.fid.Fid.Vnode & 1) || (vType(tvc) == VDIR))) {
344                     osi_dnlc_purgedp(tvc);
345                 }
346                 afs_DequeueCallback(tvc);
347             }
348         }
349     }
350
351     ReleaseWriteLock(&afs_xcbhash);
352 }
353
354 /* afs_InitCBQueue
355  *  called to initialize static and global variables associated with
356  *  the Callback expiration management mechanism.
357  */
358 void
359 afs_InitCBQueue(int doLockInit)
360 {
361     register int i;
362
363     memset((char *)cbHashT, 0, CBHTSIZE * sizeof(struct bucket));
364     for (i = 0; i < CBHTSIZE; i++) {
365         QInit(&(cbHashT[i].head));
366         /* Lock_Init(&(cbHashT[i].lock)); only if you want lots of locks, which 
367          * don't seem too useful at present.  */
368     }
369     base = 0;
370     basetime = osi_Time();
371     if (doLockInit)
372         Lock_Init(&afs_xcbhash);
373 }
374
375 /* Because there are no real-time guarantees, and especially because a
376  * thread may wait on a lock indefinitely, this routine has to be
377  * careful that it doesn't get permanently out-of-date.  Important
378  * assumption: this routine is only called from afs_Daemon, so there
379  * can't be more than one instance of this running at any one time.
380  * Presumes that basetime is never 0, and is always sane. 
381  *
382  * Before calling this routine, be sure that the first slot is pretty
383  * empty.  This -20 is because the granularity of the checks in
384  * afs_Daemon is pretty large, so I'd rather err on the side of safety
385  * sometimes.  The fact that I only bump basetime by CBHTSLOTLEN-1
386  * instead of the whole CBHTSLOTLEN is also for "safety".
387  * Conceptually, it makes this clock run just a little faster than the
388  * clock governing which slot a callback gets hashed into.  Both of these 
389  * things make CheckCallbacks work a little harder than it would have to 
390  * if I wanted to cut things finer.
391  * Everything from the old first slot is carried over into the new first
392  * slot.  Thus, if there were some things that ought to have been invalidated,
393  * but weren't (say, if the server was down), they will be examined at every
394  * opportunity thereafter.
395  */
396 int
397 afs_BumpBase(void)
398 {
399     afs_uint32 now;
400     int didbump;
401     u_int oldbase;
402
403     ObtainWriteLock(&afs_xcbhash, 87);
404     didbump = 0;
405     now = osi_Time();
406     while (basetime + (CBHTSLOTLEN - 20) <= now) {
407         oldbase = base;
408         basetime += CBHTSLOTLEN - 1;
409         base = (base + 1) % CBHTSIZE;
410         didbump++;
411         if (!QEmpty(&(cbHashT[oldbase].head))) {
412             QCat(&(cbHashT[oldbase].head), &(cbHashT[base].head));
413         }
414     }
415     ReleaseWriteLock(&afs_xcbhash);
416
417     return didbump;
418 }