Initial IBM OpenAFS 1.0 tree
[openafs.git] / src / viced / callback.c
1 /* Copyright (C) 1998 Transarc Corporation - All rights reserved */
2 /*
3  * For copyright information, see IPL which you accepted in order to
4  * download this software.
5  *
6  */
7
8 /*
9  * NEW callback package callback.c (replaces vicecb.c)
10  * Updated call back routines, NOW with:
11  * 
12  *     Faster DeleteVenus (Now called DeleteAllCallBacks)
13  *     Call back breaking for volumes
14  *     Adaptive timeouts on call backs
15  *     Architected for Multi RPC
16  *     No locks (currently implicit vnode locks--these will go, to)
17  *     Delayed call back when rpc connection down.
18  *     Bulk break of delayed call backs when rpc connection
19  *         reestablished
20  *     Strict limit on number of call backs.
21  *
22  * InitCallBack(nblocks)
23  *     Initialize: nblocks is max number # of file entries + # of callback entries
24  *     nblocks must be < 65536
25  *     Space used is nblocks*16 bytes
26  *     Note that space will be reclaimed by breaking callbacks of old hosts
27  * 
28  * time = AddCallBack(host, fid)
29  *     Add a call back.
30  *     Returns the expiration time at the workstation.
31  * 
32  * BreakCallBack(host, fid)
33  *     Break all call backs for fid, except for the specified host.
34  *     Delete all of them.
35  * 
36  * BreakVolumeCallBacks(volume)
37  *     Break all call backs on volume, using single call to each host
38  *     Delete all the call backs.
39  * 
40  * DeleteCallBack(host,fid)
41  *     Delete (do not break) single call back for fid.
42  * 
43  * DeleteFileCallBacks(fid)
44  *     Delete (do not break) all call backs for fid.
45  *
46  * DeleteAllCallBacks(host)
47  *     Delete (do not break) all call backs for host.
48  * 
49  * CleanupTimedOutCallBacks()
50  *     Delete all timed out call back entries
51  *     Must be called periodically by file server.
52  * 
53  * BreakDelayedCallBacks(host)
54  *     Break all delayed call backs for host.
55  *     Returns 1: one or more failed, 0: success.
56  * 
57  * PrintCallBackStats()
58  *     Print statistics about call backs to stdout.
59  * 
60  * DumpCallBacks() ---wishful thinking---
61  *     Dump call back state to /tmp/callback.state.
62  *     This is separately interpretable by the program pcb.
63  *
64  * Notes:  In general, if a call back to a host doesn't get through,
65  * then HostDown, supplied elsewhere, is called.  BreakDelayedCallBacks,
66  * however, does not call HostDown, but instead returns an indication of
67  * success if all delayed call backs were finally broken.
68  *
69  * BreakDelayedCallBacks MUST be called at the first sign of activity
70  * from the host after HostDown has been called (or a previous
71  * BreakDelayedCallBacks failed). The BreakDelayedCallBacks must be
72  * allowed to complete before any requests from that host are handled.
73  * If BreakDelayedCallBacks fails, then the host should remain
74  * down (and the request should be failed).
75
76  * CleanupCallBacks MUST be called periodically by the file server for
77  * this package to work correctly.  Every 5 minutes is suggested.
78  */
79
80 #include <afs/param.h>
81 #include <stdio.h> 
82 #include <stdlib.h>      /* for malloc() */
83 #include <time.h>        /* ANSI standard location for time stuff */
84 #ifdef AFS_NT40_ENV
85 #include <fcntl.h>
86 #include <io.h>
87 #else
88 #include <sys/time.h>
89 #include <sys/file.h>
90 #endif
91 #include <afs/assert.h>
92
93 #include <afs/stds.h>
94
95 #include <afs/nfs.h>   /* yuck.  This is an abomination. */
96 #include <lwp.h>
97 #include <rx/rx.h>
98 #include <afs/afscbint.h>
99 #include <afs/afsutil.h>
100 #include <lock.h>
101 #include <afs/ihandle.h>
102 #include <afs/vnode.h>
103 #include <afs/volume.h>
104 #include "viced.h"
105
106 #include <afs/ptclient.h>  /* need definition of prlist for host.h */
107 #include "host.h"
108
109
110 extern int hostCount;
111 int ShowProblems = 1;
112
113 /* Maximum number of call backs to break at once, single fid */
114 /* There is some debate as to just how large this value should be */
115 /* Ideally, it would be very very large, but I am afraid that the */
116 /* cache managers will all send in their responses simultaneously, */
117 /* thereby swamping the file server.  As a result, something like */
118 /* 10 or 15 might be a better bet. */
119 #define MAX_CB_HOSTS    10
120
121 /* max time to break a callback, otherwise client is dead or net is hosed */
122 #define MAXCBT 25  
123
124 #define u_short unsigned short
125 #define u_byte  unsigned char
126
127 struct cbcounters cbstuff;
128
129 struct cbstruct {
130   struct host * hp;
131   u_short thead;
132 } ;
133
134 struct FileEntry {
135     afs_uint32      vnode;      /* XXX This was u_short XXX */
136     afs_uint32         unique;
137     afs_uint32      volid;
138     u_short         fnext;
139     u_short         ncbs;
140     u_short         firstcb;
141     u_short         spare;
142 #ifdef  AFS_ALPHA_ENV
143     u_short         spare1;
144     u_short         spare2;
145 #endif
146 } *FE;  /* Don't use FE[0] */
147
148 struct CallBack {
149     u_short         cnext;              /* Next call back entry */
150     u_short         fhead;              /* Head of this call back chain */
151     u_byte          thead;              /* Head of timeout chain */
152     u_byte          status;             /* Call back status; see definitions, below */
153     u_short         hhead;              /* Head of host table chain */
154     u_short         tprev, tnext;       /* Timeout chain */
155     u_short         hprev, hnext;       /* Chain from host table */
156 } *CB;  /* Don't use CB[0] */
157
158 /* status bits for status field of CallBack structure */
159 #define CB_NORMAL   1   /* Normal call back */
160 #define CB_DELAYED  2   /* Delayed call back due to rpc problems.
161                         The call back entry will be added back to the
162                         host list at the END of the list, so that
163                         searching backwards in the list will find all
164                         the (consecutive)host. delayed call back entries */
165 #define CB_VOLUME   3   /* Callback for a volume */
166 #define CB_BULK     4   /* Normal callbacks, handed out from FetchBulkStatus */
167
168 /* call back indices to pointers, and vice-versa */
169 #define itocb(i)    ((i)?CB+(i):0)
170 #define cbtoi(cbp)  (!(cbp)?0:(cbp)-CB)
171
172 /* file entry indices to pointers, and vice-versa */
173 #define itofe(i)    ((i)?FE+(i):0)
174 #define fetoi(fep)  (!(fep)?0:(fep)-FE)
175
176 /* Timeouts:  there are 128 possible timeout values in effect at any
177  * given time.  Each timeout represents timeouts in an interval of 128
178  * seconds.  So the maximum timeout for a call back is 128*128=16384
179  * seconds, or 4 1/2 hours.  The timeout cleanup stuff is called only
180  * if space runs out or by the file server every 5 minutes.  This 5
181  * minute slack should be allowed for--so a maximum time of 4 hours
182  * is safer.
183  *
184  * Timeouts must be chosen to correspond to an exact multiple
185  * of 128, because all times are truncated to a 128 multiple, and
186  * timed out if the current truncated time is <= to the truncated time
187  * corresponding to the timeout queue.
188  */
189
190 /* Unix time to Call Back time, and vice-versa.  Call back time is
191    in units of 128 seconds, corresponding to time queues. */
192 #define CBtime(uxtime)  ((uxtime)>>7)
193 #define UXtime(cbtime)  ((cbtime)<<7)
194
195 /* Given a Unix time, compute the closest Unix time that corresponds to
196    a time queue, rounding up */
197 #define TimeCeiling(uxtime)     (((uxtime)+127)&~127)
198
199 /* Time to live for call backs depends upon number of users of the file.
200  * TimeOuts is indexed by this number/8 (using TimeOut macro).  Times
201  * in this table are for the workstation; server timeouts, add
202  * ServerBias */
203
204 static int TimeOuts[] = {
205 /* Note: don't make the first entry larger than 4 hours (see above) */
206     4*60*60,    /* 0-7 users */
207     1*60*60,    /* 8-15 users */
208     30*60,      /* 16-23 users */
209     15*60,      /* 24-31 users */
210     15*60,      /* 32-39 users */
211     10*60,      /* 40-47 users */
212     10*60,      /* 48-55 users */
213     10*60,      /* 56-63 users */
214 };  /* Anything more: MinTimeOut */
215
216 /* minimum time given for a call back */
217 static int MinTimeOut = (7*60);
218
219 #define TimeOutCutoff   ((sizeof(TimeOuts)/sizeof(TimeOuts[0]))*8)
220 #define TimeOut(nusers)  ((nusers)>=TimeOutCutoff? MinTimeOut: TimeOuts[(nusers)>>3])
221
222 /* time out at server is 3 minutes more than ws */
223 #define ServerBias        (3*60)
224
225 /* Heads of CB queues; a timeout index is 1+index into this array */
226 static u_short timeout[128];
227
228 /* Convert cbtime to timeout queue index */
229 #define TIndex(cbtime)  (((cbtime)&127)+1)
230
231 /* Convert cbtime to pointer to timeout queue head */
232 #define THead(cbtime)   (&timeout[TIndex(cbtime)-1])
233
234 static afs_int32 tfirst;        /* cbtime of oldest unexpired call back time queue */
235
236 /* Normalize index into timeout array so that two such indices will be
237    ordered correctly, so that they can be compared to see which times
238    sooner, or so that the difference in time out times between them
239    can be computed. */
240 #define TNorm(index)   ((index)<TIndex(tfirst)?(index)+128:(index))
241
242 /* This converts a timeout index into the actual time it will expire */
243 #define TIndexToTime(index) (UXtime(TNorm(index) - TIndex(tfirst) + tfirst))
244
245
246 /* Convert pointer to timeout queue head to index, and vice versa */
247 #define ttoi(t)         ((t-timeout)+1)
248 #define itot(i)         ((timeout)+(i-1))
249
250 /* 16 byte object get/free routines */
251 struct object {
252     struct object *next;
253 };
254
255 struct CallBack *CBfree = 0;
256 struct FileEntry *FEfree = 0;
257
258 static struct CallBack *iGetCB(register int *nused);
259 #define GetCB() ((struct CallBack *)iGetCB(&cbstuff.nCBs))
260 static struct FileEntry *iGetFE(register int *nused);
261 #define GetFE() ((struct FileEntry *)iGetFE(&cbstuff.nFEs))
262 #define FreeCB(cb) iFreeCB((struct CallBack *)cb, &cbstuff.nCBs)
263 #define FreeFE(fe) iFreeFE((struct FileEntry *)fe, &cbstuff.nFEs)
264
265 #define VHASH 512       /* Power of 2 */
266 static u_short HashTable[VHASH]; /* File entry hash table */
267 #define VHash(volume, unique) (((volume)+(unique))&(VHASH-1))
268
269 static struct FileEntry *FindFE (fid)
270     register AFSFid *fid;
271
272 {
273     int hash;
274     register fei;
275     register struct FileEntry *fe;
276
277     hash = VHash(fid->Volume, fid->Unique);
278     for (fei=HashTable[hash]; fei; fei = fe->fnext) {
279         fe = itofe(fei);
280         if (fe->volid == fid->Volume && fe->unique == fid->Unique &&
281             fe->vnode == fid->Vnode) 
282             return fe;
283     }
284     return 0;
285
286 } /*FindFE*/
287
288
289 #ifndef INTERPRET_DUMP
290
291 extern void ShutDown();
292 static CDelPtr(), FDel(), AddCallback1(), GetSomeSpace_r(); 
293
294 static struct CallBack *iGetCB(register int *nused)
295 {
296     register struct CallBack *ret;
297
298     if (ret = CBfree) {
299         CBfree = (struct CallBack *)(((struct object *)ret)->next);
300         (*nused)++;
301     }
302     return ret;
303
304 } /*cb_GetCB*/
305
306
307 static iFreeCB(cb, nused)
308     register struct CallBack *cb;
309     register int *nused;
310
311 {
312     ((struct object *)cb)->next = (struct object *)CBfree;
313     CBfree = cb;
314     (*nused)--;
315 } /*FreeCB*/
316
317
318 static struct FileEntry *iGetFE(register int *nused)
319 {
320     register struct FileEntry *ret;
321
322     if (ret = FEfree) {
323         FEfree = (struct FileEntry *)(((struct object *)ret)->next);
324         (*nused)++;
325     }
326     return ret;
327
328 } /*cb_GetFE*/
329
330
331 static iFreeFE(fe, nused)
332     register struct FileEntry *fe;
333     register int *nused;
334
335 {
336     ((struct object *)fe)->next = (struct object *)FEfree;
337     FEfree = fe;
338     (*nused)--;
339 } /*FreeFE*/
340
341
342 /* Add cb to end of specified timeout list */
343 static TAdd(cb, thead)
344     register struct CallBack *cb;
345     register u_short *thead;
346
347 {
348     if (!*thead) {
349         (*thead) = cb->tnext = cb->tprev = cbtoi(cb);
350     } else {
351         register struct CallBack *thp = itocb(*thead);
352
353         cb->tprev = thp->tprev;
354         cb->tnext = *thead;
355         thp->tprev = (itocb(thp->tprev)->tnext = cbtoi(cb));
356     }
357     cb->thead = ttoi(thead);
358
359 } /*TAdd*/
360
361
362 /* Delete call back entry from timeout list */
363 static TDel(cb)
364     register struct CallBack *cb;
365
366 {
367     register u_short *thead = itot(cb->thead);
368
369     if (*thead == cbtoi(cb))
370         *thead = (*thead == cb->tnext? 0: cb->tnext);
371     itocb(cb->tprev)->tnext = cb->tnext;
372     itocb(cb->tnext)->tprev = cb->tprev;
373
374 } /*TDel*/
375
376
377 /* Add cb to end of specified host list */
378 static HAdd(cb, host)
379     register struct CallBack *cb;
380     register struct host *host;
381
382 {
383     cb->hhead = h_htoi(host);
384     if (!host->cblist) {
385         host->cblist = cb->hnext = cb->hprev = cbtoi(cb);
386     }
387     else {
388         register struct CallBack *hhp = itocb(host->cblist);
389
390         cb->hprev = hhp->hprev;
391         cb->hnext = host->cblist;
392         hhp->hprev = (itocb(hhp->hprev)->hnext = cbtoi(cb));
393     }
394
395 } /*HAdd*/
396
397
398 /* Delete call back entry from host list */
399 static HDel(cb)
400     register struct CallBack *cb;
401
402 {
403     register u_short *hhead = &h_itoh(cb->hhead)->cblist;
404
405     if (*hhead == cbtoi(cb))
406         *hhead = (*hhead == cb->hnext? 0: cb->hnext);
407     itocb(cb->hprev)->hnext = cb->hnext;
408     itocb(cb->hnext)->hprev = cb->hprev;
409
410 } /*HDel*/
411
412
413 /* Delete call back entry from fid's chain of cb's */
414 /* N.B.  This one also deletes the CB, and also possibly parent FE, so
415  * make sure that it is not on any other list before calling this
416  * routine */
417 static CDel(cb)
418     struct CallBack *cb;
419
420 {
421     int cbi = cbtoi(cb);
422     struct FileEntry *fe = itofe(cb->fhead);
423     register u_short *cbp;
424     register int safety;
425
426     for (safety = 0, cbp = &fe->firstcb; *cbp && *cbp != cbi; 
427          cbp = &itocb(*cbp)->cnext, safety++) {
428       if (safety > cbstuff.nblks + 10) {
429         assert(0);
430         ViceLog(0,("CDel: Internal Error -- shutting down: wanted %d from %d, now at %d\n",cbi,fe->firstcb,*cbp));
431         DumpCallBackState();
432         ShutDown();
433       }
434     }
435     CDelPtr(fe, cbp);
436
437 } /*CDel*/
438
439
440 /* Same as CDel, but pointer to parent pointer to CB entry is passed,
441  * as well as file entry */
442 /* N.B.  This one also deletes the CB, and also possibly parent FE, so
443  * make sure that it is not on any other list before calling this
444  * routine */
445 int Ccdelpt=0, CcdelB=0;
446
447 static CDelPtr(fe, cbp)
448     register struct FileEntry *fe;
449     register u_short *cbp;
450
451 {
452     register struct CallBack *cb;
453
454     if (!*cbp)
455         return;
456     Ccdelpt++;
457     cb = itocb(*cbp);
458     if (cb != &CB[*cbp]) 
459         CcdelB++;
460     *cbp = cb->cnext;
461     FreeCB(cb);
462     if (--fe->ncbs == 0)
463         FDel(fe);
464
465 } /*CDelPtr*/
466
467
468 static u_short *FindCBPtr(fe, host)
469     struct FileEntry *fe;
470     struct host *host;
471
472 {
473     register afs_uint32 hostindex = h_htoi(host);
474     register struct CallBack *cb;
475     register u_short *cbp;
476     register int safety;
477
478     for (safety = 0, cbp = &fe->firstcb; *cbp; cbp = &cb->cnext, safety++) {
479         if (safety > cbstuff.nblks) {
480           ViceLog(0,("FindCBPtr: Internal Error -- shutting down.\n"));
481           DumpCallBackState();
482           ShutDown();
483         }
484         cb = itocb(*cbp);
485         if (cb->hhead == hostindex)
486             break;
487     }
488     return cbp;
489
490 } /*FindCBPtr*/
491
492
493
494 /* Delete file entry from hash table */
495 static FDel(fe)
496     register struct FileEntry *fe;
497
498 {
499     register int fei = fetoi(fe);
500     register unsigned short *p = &HashTable[VHash(fe->volid, fe->unique)];
501
502     while (*p && *p != fei)
503         p = &itofe(*p)->fnext;
504     assert(*p);
505     *p = fe->fnext;
506     FreeFE(fe);
507
508 } /*FDel*/
509
510
511
512 InitCallBack(nblks)
513     int nblks;
514 {
515
516     H_LOCK
517     tfirst = CBtime(FT_ApproxTime());
518     /* N.B. FE's, CB's share same free list.  If the sizes of either change,
519       FE and CB will have to be separated.  The "-1", below, is because
520       FE[0] and CB[0] are not used--and not allocated */
521     FE = ((struct FileEntry *)(malloc(sizeof(struct FileEntry)*nblks)))-1;
522     cbstuff.nFEs = nblks;
523     while (cbstuff.nFEs)
524         FreeFE(&FE[cbstuff.nFEs]); /* This is correct */
525     CB = ((struct CallBack *)(malloc(sizeof(struct CallBack)*nblks)))-1;
526     cbstuff.nCBs = nblks;
527     while (cbstuff.nCBs)
528         FreeCB(&CB[cbstuff.nCBs]); /* This is correct */
529     cbstuff.nblks = nblks;
530     cbstuff.nbreakers = 0;
531     H_UNLOCK
532
533 } /*InitCallBack*/
534
535
536 afs_int32 XCallBackBulk_r(ahost, fids, nfids)
537     struct host *ahost;
538     struct AFSFid *fids;
539     afs_int32 nfids;
540
541 {
542     struct AFSCallBack tcbs[AFSCBMAX];
543     register int i;
544     struct AFSCBFids tf;
545     struct AFSCBs tc;
546     int code;
547     int j;
548
549 #ifdef  ADAPT_MTU
550     rx_SetConnDeadTime(ahost->callback_rxcon, 4); 
551     rx_SetConnHardDeadTime(ahost->callback_rxcon, AFS_HARDDEADTIME);
552 #endif
553
554     code = 0;
555     j = 0;
556     while (nfids > 0) {
557
558       for(i=0;i<nfids && i < AFSCBMAX;i++) {
559         tcbs[i].CallBackVersion = CALLBACK_VERSION;
560         tcbs[i].ExpirationTime = 0;
561         tcbs[i].CallBackType = CB_DROPPED;
562       }
563       tf.AFSCBFids_len = i;
564       tf.AFSCBFids_val = &(fids[j]);
565       nfids -= i;
566       j += i;
567       tc.AFSCBs_len = i;
568       tc.AFSCBs_val = tcbs;
569
570       H_UNLOCK
571       code |= RXAFSCB_CallBack(ahost->callback_rxcon, &tf, &tc);
572       H_LOCK
573     }
574
575     return code;
576 } /*XCallBackBulk*/
577
578
579 /* the locked flag tells us if the host entry has already been locked 
580  * by our parent.  I don't think anybody actually calls us with the
581  * host locked, but here's how to make that work:  GetSomeSpace has to
582  * change so that it doesn't attempt to lock any hosts < "host".  That
583  * means that it might be unable to free any objects, so it has to
584  * return an exit status.  If it fails, then AddCallBack1 might fail,
585  * as well. If so, the host->ResetDone should probably be set to 0,
586  * and we probably don't want to return a callback promise to the
587  * cache manager, either. */
588 int
589 AddCallBack1(host, fid, thead, type, locked)
590     struct host *host;
591     AFSFid *fid;
592     int locked;
593     int type;
594     u_short *thead;
595 {
596     int retVal;
597     H_LOCK
598     if ( !locked ) {
599         h_Lock_r(host);
600     }
601     retVal = AddCallBack1_r(host, fid, thead, type, 1);
602
603     if ( !locked ) {
604         h_Unlock_r(host);
605     }
606     H_UNLOCK
607     return retVal;
608 }
609
610 int
611 AddCallBack1_r(host, fid, thead, type, locked)
612     struct host *host;
613     AFSFid *fid;
614     int locked;
615     int type;
616     u_short *thead;
617 {
618     struct FileEntry *fe;
619     struct CallBack *cb = 0, *lastcb = 0;
620     struct FileEntry *newfe = 0;
621     afs_uint32 time_out;
622     u_short *Thead = thead;
623     struct CallBack *newcb = 0;
624     int safety;
625     
626     host->Console |= 2;
627
628     /* allocate these guys first, since we can't call the allocator with
629        the host structure locked -- or we might deadlock. However, we have 
630        to avoid races with FindFE... */
631     while (!(newcb = GetCB())) {
632         GetSomeSpace_r(host, locked);
633     }
634     while(!(newfe = GetFE())) {  /* Get it now, so we don't have to call */
635       /* GetSomeSpace with the host locked, later.  This might turn out to */
636       /* have been unneccessary, but that's actually kind of unlikely, since */
637       /* most files are not shared. */
638       GetSomeSpace_r(host, locked);
639     }
640
641     if (!locked) {
642       h_Lock_r(host);  /* this can yield, so do it before we get any */
643                      /* fragile info */
644     }
645
646     fe = FindFE(fid);
647     if (type == CB_NORMAL) {
648         time_out = TimeCeiling(FT_ApproxTime()+TimeOut(fe?fe->ncbs:0)+ServerBias);
649         Thead = THead(CBtime(time_out));
650     }
651     else if (type == CB_VOLUME) {
652         time_out = TimeCeiling((60*120+FT_ApproxTime())+ServerBias);
653         Thead = THead(CBtime(time_out));
654     }
655     else if (type == CB_BULK) {
656       /* bulk status can get so many callbacks all at once, and most of them
657        * are probably not for things that will be used for long.
658        */
659         time_out = TimeCeiling(FT_ApproxTime() + ServerBias 
660                                + TimeOut(22 + (fe?fe->ncbs:0)));
661         Thead = THead(CBtime(time_out));
662     }
663
664     host->Console &= ~2;
665
666     if (!fe) {
667         register afs_uint32 hash;
668
669         fe = newfe;
670         newfe = (struct FileEntry *) 0;
671         fe->firstcb = 0;
672         fe->volid = fid->Volume;
673         fe->vnode = fid->Vnode;
674         fe->unique = fid->Unique;
675         fe->ncbs = 0;
676         hash = VHash(fid->Volume, fid->Unique);
677         fe->fnext = HashTable[hash];
678         HashTable[hash] = fetoi(fe);
679     }
680     for (safety = 0, lastcb = cb = itocb(fe->firstcb); cb;
681          lastcb = cb, cb = itocb(cb->cnext), safety++) {
682         if (safety > cbstuff.nblks) {
683           ViceLog(0,("AddCallBack1: Internal Error -- shutting down.\n"));
684           DumpCallBackState();
685           ShutDown();
686         }
687         if (cb->hhead == h_htoi(host))
688             break;
689     }
690     if (cb) {/* Already have call back:  move to new timeout list */
691         /* don't change delayed callbacks back to normal ones */
692         if (cb->status != CB_DELAYED)
693             cb->status = type;
694         /* Only move if new timeout is longer */
695         if (TNorm(ttoi(Thead)) > TNorm(cb->thead)) {
696             TDel(cb);
697             TAdd(cb, Thead);
698         }
699     } else {
700         cb = newcb;
701         newcb = (struct CallBack *) 0;
702         *(lastcb?&lastcb->cnext:&fe->firstcb) = cbtoi(cb);
703         fe->ncbs++;
704         cb->cnext = 0;
705         cb->fhead = fetoi(fe);
706         cb->status = type;
707         HAdd(cb, host);
708         TAdd(cb, Thead);
709     }
710
711     /* now free any still-unused callback or host entries */
712     if (newcb) FreeCB(newcb);
713     if (newfe) FreeFE(newfe);
714
715     if (!locked)         /* freecb and freefe might(?) yield */
716         h_Unlock_r(host);
717
718     if (type == CB_NORMAL || type == CB_VOLUME || type == CB_BULK ) 
719         return time_out-ServerBias; /* Expires sooner at workstation */
720
721 return 0;
722 } /*AddCallBack1*/
723
724
725 /* Take an array full of hosts, all held.  Break callbacks to them, and 
726  * release the holds once you're done, except don't release xhost.  xhost 
727  * may be NULL.  Currently only works for a single Fid in afidp array.
728  * If you want to make this work with multiple fids, you need to fix
729  * the error handling.  One approach would be to force a reset if a
730  * multi-fid call fails, or you could add delayed callbacks for each
731  * fid.   You probably also need to sort and remove duplicate hosts.
732  * When this is called from the BreakVolumeCallBacks path, it does NOT 
733  * force a reset if the RPC fails, it just marks the host down and tries 
734  * to create a delayed callback. */
735 /* N.B.  be sure that code works when ncbas == 0 */
736 /* N.B.  requires all the cba[*].hp pointers to be valid... */
737 /* This routine does not hold a lock on the host for the duration of 
738  * the BreakCallBack RPC, which is a significant deviation from tradition.
739  * It _does_ get a lock on the host before setting VenusDown = 1,
740  * which is sufficient only if VenusDown = 0 only happens when the
741  * lock is held over the RPC and the subsequent VenusDown == 0
742  * wherever that is done. */
743 static void MultiBreakCallBack_r(cba, ncbas, afidp, xhost)
744      struct cbstruct cba[];
745      int ncbas;
746      struct AFSCBFids *afidp;
747      struct host * xhost;
748 {
749   int i,j;
750   struct rx_connection *conns[MAX_CB_HOSTS];
751   int opt_TO;  /* secs, but internal adaptive parms are in ms */
752   static struct AFSCBs tc = {0,0};
753
754   assert(ncbas <= MAX_CB_HOSTS);
755
756   /* set up conns for multi-call */
757   for (i=0,j=0; i<ncbas; i++) {
758     struct host *thishost = cba[i].hp;
759     if (!thishost || (thishost->hostFlags & HOSTDELETED)) {
760       continue;
761     }
762     conns[j++] = thishost->callback_rxcon;
763         
764 #ifdef  ADAPT_MTU
765     rx_SetConnDeadTime (thishost->callback_rxcon, 4); 
766     rx_SetConnHardDeadTime (thishost->callback_rxcon, AFS_HARDDEADTIME); 
767 #endif
768   }
769   
770   if (j) {            /* who knows what multi would do with 0 conns? */
771     cbstuff.nbreakers++;
772   H_UNLOCK
773   multi_Rx(conns, j) {
774     multi_RXAFSCB_CallBack(afidp, &tc);
775     if (multi_error) {
776       unsigned short idx ;
777       struct host *hp;
778       idx = 0;
779       /* If there's an error, we have to hunt for the right host. 
780        * The conns array _should_ correspond one-to-one to the cba
781        * array, except in some rare cases it might be missing one 
782        * or more elements.  So the optimistic case is almost 
783        * always right.  At worst, it's the starting point for the 
784        * hunt. */
785       for (hp=0,i=multi_i;i<j;i++) { 
786         hp = cba[i].hp;   /* optimistic, but usually right */
787         if (!hp) {
788           break;
789         }
790         if (conns[multi_i] == hp->callback_rxcon) {
791           idx = cba[i].thead;
792           break;
793         }
794       }
795       
796       if (!hp) {
797         ViceLog(0, ("BCB: INTERNAL ERROR: hp=%x, cba=%x\n",hp,cba));
798       }
799       else {
800         /* 
801         ** try breaking callbacks on alternate interface addresses
802         */
803         if ( MultiBreakCallBackAlternateAddress(hp, afidp) )
804         {
805           if (ShowProblems) {
806                 ViceLog(7, 
807                   ("BCB: Failed on file %u.%d.%d, host %x.%d is down\n",
808                    afidp->AFSCBFids_val->Volume, afidp->AFSCBFids_val->Vnode,
809                    afidp->AFSCBFids_val->Unique, hp->host, hp->port));
810                 }
811
812                 H_LOCK
813                 h_Lock_r(hp);
814                 hp->hostFlags |= VENUSDOWN;
815                 /**
816                   * We always go into AddCallBack1_r with the host locked
817                   */
818                 AddCallBack1_r(hp,afidp->AFSCBFids_val,itot(idx), CB_DELAYED, 1);
819                 h_Unlock_r(hp);
820                 H_UNLOCK
821         }
822       }
823     }
824   } multi_End;
825   H_LOCK
826   cbstuff.nbreakers--;
827   }
828   
829   for (i=0; i<ncbas; i++) {
830     struct host *hp;
831     hp = cba[i].hp;
832     if (hp && xhost != hp)
833       h_Release_r(hp);
834   }
835
836 return ;
837 }
838
839 /*
840  * Break all call backs for fid, except for the specified host (unless flag
841  * is true, in which case all get a callback message. Assumption: the specified
842  * host is h_Held, by the caller; the others aren't.
843  * Specified host may be bogus, that's ok.  This used to check to see if the 
844  * host was down in two places, once right after the host was h_held, and 
845  * again after it was locked.  That race condition is incredibly rare and
846  * relatively harmless even when it does occur, so we don't check for it now. 
847  */
848 BreakCallBack(xhost, fid, flag)
849     struct host *xhost;
850     int flag;  /* if flag is true, send a break callback msg to "host", too */
851     AFSFid *fid;
852 {
853     struct FileEntry *fe;
854     struct CallBack *cb, *nextcb;
855     struct cbstruct cba[MAX_CB_HOSTS];
856     int ncbas;
857     struct rx_connection *conns[MAX_CB_HOSTS];
858     struct AFSCBFids tf;
859     int hostindex;
860
861     ViceLog(7,("BCB: BreakCallBack(all but %x.%d, (%u,%d,%d))\n",
862                xhost->host, xhost->port, fid->Volume, fid->Vnode, 
863                fid->Unique));
864
865     H_LOCK
866
867     cbstuff.BreakCallBacks++; 
868     fe = FindFE(fid);
869     if (!fe) {
870       goto done;
871     }
872     hostindex = h_htoi(xhost);
873     cb = itocb(fe->firstcb);
874     if (!cb || ((fe->ncbs == 1) && (cb->hhead == hostindex) && !flag)) {
875       /* the most common case is what follows the || */
876       goto done;
877     }
878     tf.AFSCBFids_len = 1;
879     tf.AFSCBFids_val = fid;
880
881     for(;cb;) { 
882       for (ncbas=0; cb && ncbas<MAX_CB_HOSTS; cb=nextcb) {
883         nextcb = itocb(cb->cnext);
884         if ((cb->hhead != hostindex || flag) 
885             && (cb->status == CB_BULK || cb->status == CB_NORMAL
886                 || cb->status == CB_VOLUME) ) {
887           struct host *thishost = h_itoh(cb->hhead);
888           if (!thishost) {
889             ViceLog(0,("BCB: BOGUS! cb->hhead is NULL!\n"));
890           }
891           else if (thishost->hostFlags & VENUSDOWN) {
892             ViceLog(7,("BCB: %x.%d is down; delaying break call back\n",
893                        thishost->host, thishost->port));
894             cb->status = CB_DELAYED;
895           }
896           else {
897             h_Hold_r(thishost);
898             cba[ncbas].hp = thishost;
899             cba[ncbas].thead = cb->thead;
900             ncbas++;
901             TDel(cb);
902             HDel(cb);
903             CDel(cb); /* Usually first; so this delete */
904                       /* is reasonably inexpensive */
905           }
906         }
907       }
908       
909       if (ncbas) {
910         MultiBreakCallBack_r(cba, ncbas, &tf, xhost);
911
912         /* we need to to all these initializations again because MultiBreakCallBack may block */
913         fe = FindFE(fid); 
914         if (!fe) {
915                 goto done;
916         }
917         cb = itocb(fe->firstcb);
918         if (!cb || ((fe->ncbs == 1) && (cb->hhead == hostindex) && !flag)) {
919                 /* the most common case is what follows the || */
920                 goto done;
921         }
922       }
923     }
924
925   done:
926     H_UNLOCK
927     return;
928 } /*BreakCallBack*/
929
930
931 /* Delete (do not break) single call back for fid */
932 DeleteCallBack(host, fid)
933     struct host *host;
934     AFSFid *fid;
935
936 {
937     register struct FileEntry *fe;
938     register u_short *pcb;
939     cbstuff.DeleteCallBacks++;
940     
941     H_LOCK
942     h_Lock_r(host);
943     fe = FindFE(fid);
944     if (!fe) {
945         h_Unlock_r(host);
946         H_UNLOCK
947         ViceLog(8,("DCB: No call backs for fid (%u, %d, %d)\n",
948             fid->Volume, fid->Vnode, fid->Unique));
949         return;
950     }
951     pcb = FindCBPtr(fe, host);
952     if (!*pcb) {
953         ViceLog(8,("DCB: No call back for host %x.%d, (%u, %d, %d)\n",
954             host->host, host->port, fid->Volume, fid->Vnode, fid->Unique));
955         h_Unlock_r(host);
956         H_UNLOCK
957         return;
958     }
959     HDel(itocb(*pcb));
960     TDel(itocb(*pcb));
961     CDelPtr(fe, pcb);
962     h_Unlock_r(host);
963     H_UNLOCK
964
965 } /*DeleteCallBack*/
966
967
968 /*
969  * Delete (do not break) all call backs for fid.  This call doesn't
970  * set all of the various host locks, but it shouldn't really matter
971  * since we're not adding callbacks, but deleting them.  I'm not sure
972  * why it doesn't set the lock, however; perhaps it should.
973  */
974 DeleteFileCallBacks(fid)
975     AFSFid *fid;
976 {
977     register struct FileEntry *fe;
978     register struct CallBack *cb;
979     register afs_uint32 cbi;
980     register n;
981
982     H_LOCK
983     cbstuff.DeleteFiles++;
984     fe = FindFE(fid);
985     if (!fe) {
986         H_UNLOCK
987         ViceLog(8,("DF: No fid (%u,%u,%u) to delete\n",
988             fid->Volume, fid->Vnode, fid->Unique));
989         return;
990     }
991     for (n=0,cbi = fe->firstcb; cbi; n++) {
992         cb = itocb(cbi);
993         cbi = cb->cnext;
994         TDel(cb);
995         HDel(cb);
996         FreeCB(cb);
997     }
998     FDel(fe);
999     H_UNLOCK
1000
1001 } /*DeleteFileCallBacks*/
1002
1003
1004 /* Delete (do not break) all call backs for host.  The host should be
1005  * locked. */
1006 DeleteAllCallBacks(host)
1007     struct host *host;
1008 {
1009     int retVal;
1010     H_LOCK
1011     retVal = DeleteAllCallBacks_r(host);
1012     H_UNLOCK
1013     return retVal;
1014 }
1015
1016 DeleteAllCallBacks_r(host)
1017     struct host *host;
1018 {
1019     register struct CallBack *cb;
1020     register int cbi, first;
1021
1022     cbstuff.DeleteAllCallBacks++;
1023     cbi = first = host->cblist;
1024     if (!cbi) {
1025         ViceLog(8,("DV: no call backs\n"));
1026         return;
1027     }
1028     do {        
1029         cb = itocb(cbi);
1030         cbi = cb->hnext;
1031         TDel(cb);
1032         CDel(cb);
1033     } while (cbi != first);
1034     host->cblist = 0;
1035
1036 } /*DeleteAllCallBacks*/
1037
1038
1039 /*
1040  * Break all delayed call backs for host.  Returns 1 if all call backs
1041  * successfully broken; 0 otherwise.  Assumes host is h_Held and h_Locked.
1042  * Must be called with VenusDown set for this host
1043  */
1044 int BreakDelayedCallBacks(host)
1045     struct host *host;
1046 {
1047     int retVal;
1048     H_LOCK
1049     retVal = BreakDelayedCallBacks_r(host);
1050     H_UNLOCK
1051     return retVal;
1052 }
1053
1054 extern afsUUID FS_HostUUID;
1055
1056 int BreakDelayedCallBacks_r(host)
1057     struct host *host;
1058 {
1059     struct AFSFid fids[AFSCBMAX];
1060     u_short thead[AFSCBMAX];
1061     int cbi, first, nfids;
1062     struct CallBack *cb;
1063     struct interfaceAddr interf;
1064     int code;
1065
1066     cbstuff.nbreakers++;
1067     if (!(host->hostFlags & RESETDONE) && !(host->hostFlags & HOSTDELETED)) {
1068         host->hostFlags &= ~ALTADDR; /* alterrnate addresses are invalid */
1069         if ( host->interface ) {
1070             H_UNLOCK
1071             code = RXAFSCB_InitCallBackState3(host->callback_rxcon,
1072                                               &FS_HostUUID);
1073             H_LOCK
1074         } else {
1075             H_UNLOCK
1076             code = RXAFSCB_InitCallBackState(host->callback_rxcon);
1077             H_LOCK
1078         }
1079         host->hostFlags |= ALTADDR; /* alternate addresses are valid */
1080         if (code) {
1081             if (ShowProblems) {
1082                 ViceLog(0,
1083            ("CB: Call back connect back failed (in break delayed) for %x.%d\n",
1084                         host->host, host->port));
1085               }
1086             host->hostFlags |= VENUSDOWN;
1087         }
1088         else {
1089             ViceLog(25,("InitCallBackState success on %x\n",host->host));
1090             /* reset was done successfully */
1091             host->hostFlags |= RESETDONE;
1092             host->hostFlags &= ~VENUSDOWN;
1093         }
1094     }
1095     else while (!(host->hostFlags & HOSTDELETED)) {
1096         nfids = 0;
1097         host->hostFlags &= ~VENUSDOWN;  /* presume up */
1098         cbi = first = host->cblist;
1099         if (!cbi) 
1100             break;
1101         do {
1102             first = host->cblist;
1103             cb = itocb(cbi);
1104             cbi = cb->hnext;
1105             if (cb->status == CB_DELAYED) {
1106                 register struct FileEntry *fe = itofe(cb->fhead);
1107                 thead[nfids] = cb->thead;
1108                 fids[nfids].Volume = fe->volid;
1109                 fids[nfids].Vnode =  fe->vnode;
1110                 fids[nfids].Unique = fe->unique;
1111                 nfids++;
1112                 HDel(cb);
1113                 TDel(cb);
1114                 CDel(cb);
1115             }
1116         } while (cbi && cbi != first && nfids < AFSCBMAX);
1117
1118         if (nfids==0) {
1119           break;
1120         }
1121
1122         if (XCallBackBulk_r(host, fids, nfids)) {
1123             /* Failed, again:  put them back, probably with old
1124              timeout values */
1125             int i;
1126             if (ShowProblems) {
1127                 ViceLog(0,
1128              ("CB: XCallBackBulk failed, host=%x.%d; callback list follows:\n",
1129                     host->host, host->port));
1130             }
1131             for (i = 0; i<nfids; i++) {
1132                 if (ShowProblems) {
1133                     ViceLog(0,
1134                     ("CB: Host %x.%d, file %u.%u.%u (part of bulk callback)\n",
1135                                host->host, host->port, 
1136                                fids[i].Volume, fids[i].Vnode, fids[i].Unique));
1137                 }
1138                 /* used to do this:
1139                    AddCallBack1_r(host, &fids[i], itot(thead[i]), CB_DELAYED, 1);
1140                  * but it turns out to cause too many tricky locking problems.
1141                  * now, if break delayed fails, screw it. */
1142             }
1143             host->hostFlags |= VENUSDOWN; /* Failed */
1144             ClearHostCallbacks_r(host, 1/* locked */);
1145             nfids = 0;
1146             break;
1147         }
1148         if (nfids < AFSCBMAX)
1149             break;
1150     }
1151
1152     cbstuff.nbreakers--;
1153     return (host->hostFlags & VENUSDOWN);
1154
1155 } /*BreakDelayedCallBacks*/
1156
1157
1158 struct VCBParams {
1159   struct cbstruct cba[MAX_CB_HOSTS];  /* re-entrant storage */
1160   unsigned int ncbas;
1161   unsigned short thead;     /* head of timeout queue for youngest callback */
1162   struct AFSFid *fid;
1163 };
1164
1165 /*
1166 ** isheld is 0 if the host is held in h_Enumerate
1167 ** isheld is 1 if the host is held in BreakVolumeCallBacks
1168 */
1169 static int MultiBreakVolumeCallBack_r (host, isheld, parms)
1170   struct host *host;
1171   int isheld;
1172   struct VCBParams *parms;
1173 {
1174     if ( !isheld )
1175         return isheld; /* host is held only by h_Enumerate, do nothing */
1176     if ( host->hostFlags & HOSTDELETED )
1177         return 0; /* host is deleted, release hold */
1178
1179     if (host->hostFlags & VENUSDOWN) {
1180         h_Lock_r(host);
1181         if (host->hostFlags & HOSTDELETED) {
1182             h_Unlock_r(host);
1183             return 0;      /* Release hold */
1184         }
1185         ViceLog(8,("BVCB: volume call back for host %x.%d failed\n",
1186                  host->host,host->port));
1187         if (ShowProblems) {
1188             ViceLog(0, ("CB: volume callback for host %x.%d failed\n",
1189                     host->host, host->port));
1190         }
1191         DeleteAllCallBacks_r(host); /* Delete all callback state rather than
1192                                      attempting to selectively remember to
1193                                      delete the volume callbacks later */
1194         host->hostFlags &= ~RESETDONE; /* Do InitCallBackState when host returns */
1195         h_Unlock_r(host);
1196         return 0; /* release hold */
1197     }
1198     assert(parms->ncbas <= MAX_CB_HOSTS);
1199
1200     /* Do not call MultiBreakCallBack on the current host structure
1201     ** because it would prematurely release the hold on the host
1202     */
1203     if ( parms->ncbas  == MAX_CB_HOSTS ) {
1204       struct AFSCBFids tf;
1205
1206       tf.AFSCBFids_len = 1;
1207       tf.AFSCBFids_val = parms->fid;
1208       
1209                                 /* this releases all the hosts */
1210       MultiBreakCallBack_r(parms->cba, parms->ncbas, &tf, 0 /* xhost */);
1211
1212       parms->ncbas = 0;
1213     }  
1214     parms->cba[parms->ncbas].hp = host;
1215     parms->cba[(parms->ncbas)++].thead = parms->thead;
1216     return 1;   /* DON'T release hold, because we still need it. */
1217 }
1218
1219 /*
1220 ** isheld is 0 if the host is held in h_Enumerate
1221 ** isheld is 1 if the host is held in BreakVolumeCallBacks
1222 */
1223 static int MultiBreakVolumeCallBack (host, isheld, parms)
1224   struct host *host;
1225   int isheld;
1226   struct VCBParams *parms;
1227 {
1228     int retval;
1229     H_LOCK
1230     retval = MultiBreakVolumeCallBack_r(host, isheld, parms);
1231     H_UNLOCK
1232     return retval;
1233 }
1234
1235 /*
1236  * Break all call backs on a single volume.  Don't call this with any
1237  * hosts h_held.  Note that this routine clears the callbacks before
1238  * actually breaking them, and that the vnode isn't locked during this
1239  * operation, so that people might see temporary callback loss while
1240  * this function is executing.  It is just a temporary state, however,
1241  * since the callback will be broken later by this same function.
1242  *
1243  * Now uses multi-RX for CallBack RPC.  Note that the
1244  * multiBreakCallBacks routine does not force a reset if the RPC
1245  * fails, unlike the previous version of this routine, but does create
1246  * a delayed callback.  Resets will be forced if the host is
1247  * determined to be down before the RPC is executed.
1248  */
1249 BreakVolumeCallBacks(volume)
1250     afs_uint32 volume;
1251
1252 {
1253     struct AFSFid fid;
1254     int hash;
1255     u_short *feip;
1256     struct CallBack *cb;
1257     struct FileEntry *fe;
1258     struct host *host;
1259     struct VCBParams henumParms;
1260     unsigned short tthead = 0;  /* zero is illegal value */
1261
1262     H_LOCK
1263     fid.Volume = volume, fid.Vnode = fid.Unique = 0;
1264     for (hash=0; hash<VHASH; hash++) {
1265         for (feip = &HashTable[hash]; fe = itofe(*feip); ) {
1266             if (fe->volid == volume) {
1267                 register struct CallBack *cbnext;
1268                 for (cb = itocb(fe->firstcb); cb; cb = cbnext) {
1269                     host = h_itoh(cb->hhead);
1270                     h_Hold_r(host);
1271                     cbnext = itocb(cb->cnext);
1272                     if (!tthead || (TNorm(tthead) < TNorm(cb->thead))) {
1273                       tthead = cb->thead;
1274                     }
1275                     TDel(cb);
1276                     HDel(cb);
1277                     FreeCB(cb);
1278                     /* leave hold for MultiBreakVolumeCallBack to clear */
1279                 }
1280                 *feip = fe->fnext;
1281                 FreeFE(fe);
1282             } else {
1283                 feip = &fe->fnext;
1284             }
1285         }
1286     }
1287
1288     if (!tthead) {
1289       /* didn't find any callbacks, so return right away. */
1290       H_UNLOCK
1291       return 0;
1292     }
1293     henumParms.ncbas = 0;
1294     henumParms.fid = &fid;
1295     henumParms.thead = tthead;
1296     H_UNLOCK
1297     h_Enumerate(MultiBreakVolumeCallBack, (char *) &henumParms);
1298     H_LOCK
1299         
1300     if (henumParms.ncbas) {    /* do left-overs */
1301       struct AFSCBFids tf;
1302       tf.AFSCBFids_len = 1;
1303       tf.AFSCBFids_val = &fid;
1304       
1305       MultiBreakCallBack_r(henumParms.cba, henumParms.ncbas, &tf, 0 );
1306
1307       henumParms.ncbas = 0;
1308     }  
1309     H_UNLOCK
1310
1311 return 0;
1312 } /*BreakVolumeCallBacks*/
1313
1314
1315 /*
1316  * Delete all timed-out call back entries (to be called periodically by file
1317  * server)
1318  */
1319 CleanupTimedOutCallBacks()
1320 {
1321     H_LOCK
1322     CleanupTimedOutCallBacks_r();
1323     H_UNLOCK
1324 }
1325
1326 CleanupTimedOutCallBacks_r()
1327 {
1328     afs_uint32 now = CBtime(FT_ApproxTime());
1329     register u_short *thead;
1330     register struct CallBack *cb;
1331     register int ntimedout = 0;
1332     extern void ShutDown();
1333
1334     while (tfirst <= now) {
1335         register int cbi;
1336         cbi = *(thead = THead(tfirst));
1337         if (cbi) {
1338             do {
1339                 cb = itocb(cbi);
1340                 cbi = cb->tnext;
1341                 ViceLog(8,("CCB: deleting timed out call back %x.%d, (%u,%u,%u)\n",
1342                         h_itoh(cb->hhead)->host, h_itoh(cb->hhead)->port, 
1343                         itofe(cb->fhead)->volid, itofe(cb->fhead)->vnode,
1344                         itofe(cb->fhead)->unique));
1345                 HDel(cb);
1346                 CDel(cb);
1347                 ntimedout++;
1348                 if (ntimedout > cbstuff.nblks) {
1349                   ViceLog(0,("CCB: Internal Error -- shutting down...\n"));
1350                   DumpCallBackState();
1351                   ShutDown();
1352                 }
1353             } while (cbi != *thead);
1354             *thead = 0;
1355         }
1356         tfirst++;
1357     }
1358     cbstuff.CBsTimedOut += ntimedout;
1359     ViceLog(7,("CCB: deleted %d timed out callbacks\n", ntimedout));
1360     return (ntimedout > 0);
1361
1362 } /*CleanupTimedOutCallBacks*/
1363
1364
1365 static struct host *lih_host;
1366
1367 static int lih_r(host, held, hostp)
1368     register struct host *host, *hostp;
1369     register int held;
1370
1371 {
1372     if (host->cblist
1373            && ((hostp && host != hostp) || (!held && !h_OtherHolds_r(host)))
1374            && (!lih_host || host->ActiveCall < lih_host->ActiveCall) ) {
1375         lih_host = host;
1376     }
1377     return held;
1378
1379 } /*lih*/
1380
1381
1382 /* This could be upgraded to get more space each time */
1383 /* first pass: find the oldest host which isn't held by anyone */
1384 /* second pass: find the oldest host who isn't "me" */
1385 /* always called with hostp unlocked */
1386 static int GetSomeSpace_r(hostp, locked)
1387     struct host *hostp;
1388     int locked;
1389 {
1390     register struct host *hp, *hp1 = (struct host *)0;
1391     int i=0;
1392
1393     cbstuff.GotSomeSpaces++;
1394     ViceLog(5,("GSS: First looking for timed out call backs via CleanupCallBacks\n"));
1395     if (CleanupTimedOutCallBacks_r()) {
1396         cbstuff.GSS3++;
1397         return;
1398     }
1399     do {
1400         lih_host = 0;
1401         h_Enumerate_r(lih_r, (char *)hp1);
1402         hp = lih_host;
1403         if (hp) {
1404             cbstuff.GSS4++;
1405             if ( ! ClearHostCallbacks_r(hp, 0 /* not locked or held */) )
1406                 return;
1407         } else {
1408             hp1 = hostp;
1409             cbstuff.GSS1++;
1410             ViceLog(5,("GSS: Try harder for longest inactive host cnt= %d\n", i));
1411             /*
1412              * Next time try getting callbacks from any host even if
1413              * it's deleted (that's actually great since we can freely
1414              * remove its callbacks) or it's held since the only other
1415              * option is starvation for the file server (i.e. until the
1416              * callback timeout arrives).
1417              */
1418             i++;
1419         }
1420     } while (i < 2);
1421     /*
1422      * No choice to clear this host's callback state
1423      */
1424     /* third pass: we still haven't gotten any space, so we free what we had
1425      * previously passed over. */
1426     cbstuff.GSS2++;
1427     if (!locked) {
1428       h_Lock_r(hostp);
1429     }
1430     ClearHostCallbacks_r(hostp, 1/*already locked*/);
1431     if (!locked) {
1432       h_Unlock_r(hostp);
1433     }
1434 } /*GetSomeSpace*/
1435
1436
1437 ClearHostCallbacks(hp, locked)
1438      struct host *hp;
1439      int locked; /* set if caller has already locked the host */
1440 {
1441     int retVal;
1442     H_LOCK
1443     retVal = ClearHostCallbacks_r(hp, locked);
1444     H_UNLOCK
1445     return retVal;
1446 }
1447
1448 int ClearHostCallbacks_r(hp, locked)
1449      struct host *hp;
1450      int locked; /* set if caller has already locked the host */
1451 {
1452     struct interfaceAddr interf;
1453     int code;
1454     int held = 0;
1455
1456     ViceLog(5,("GSS: Delete longest inactive host %x\n", hp->host));
1457     if ( !(held = h_Held_r(hp)) )
1458         h_Hold_r(hp);
1459
1460     /** Try a non-blocking lock. If the lock is already held return
1461       * after releasing hold on hp
1462       */
1463     if (!locked) {
1464        if ( h_NBLock_r(hp) ) {
1465            if ( !held )
1466                h_Release_r(hp);
1467            return 1;
1468        }
1469     }
1470     if (hp->Console & 2) {
1471         /*
1472          * If the special console field is set it means that a thread
1473          * is waiting in AddCallBack1 after it set pointers to the
1474          * file entry and/or callback entry. Because of the bogus
1475          * usage of h_hold it won't prevent from another thread, this
1476          * one, to remove all the callbacks so just to be safe we keep
1477          * a reference. NOTE, on the last phase we'll free the calling
1478          * host's callbacks but that's ok...
1479          */
1480         cbstuff.GSS5++;
1481     }
1482     DeleteAllCallBacks_r(hp);
1483     if (hp->hostFlags & VENUSDOWN) {
1484         hp->hostFlags &= ~RESETDONE;    /* remember that we must do a reset */
1485     } else {
1486         /* host is up, try a call */
1487         hp->hostFlags &= ~ALTADDR; /* alternate addresses are invalid */
1488         if (hp->interface) {
1489             H_UNLOCK
1490             code = RXAFSCB_InitCallBackState3(hp->callback_rxcon, &FS_HostUUID);
1491             H_LOCK
1492         } else {
1493             H_UNLOCK
1494             code = RXAFSCB_InitCallBackState(hp->callback_rxcon);
1495             H_LOCK
1496         }
1497         hp->hostFlags |= ALTADDR; /* alternate addresses are valid */
1498         if (code)
1499         {
1500             /* failed, mark host down and need reset */
1501             hp->hostFlags |= VENUSDOWN;
1502             hp->hostFlags &= ~RESETDONE;
1503         } else {
1504             /* reset succeeded, we're done */
1505             hp->hostFlags |= RESETDONE;
1506         }
1507     }
1508     if (!locked) {
1509        h_Unlock_r(hp);
1510     }
1511     if ( !held )
1512         h_Release_r(hp);
1513
1514     return 0;
1515 }
1516 #endif /* INTERPRET_DUMP */
1517
1518
1519 PrintCallBackStats()
1520
1521 {
1522     fprintf(stderr, "%d add CB, %d break CB, %d del CB, %d del FE, %d CB's timed out, %d space reclaim, %d del host\n",
1523             cbstuff.AddCallBacks, cbstuff.BreakCallBacks, cbstuff.DeleteCallBacks,
1524             cbstuff.DeleteFiles, cbstuff.CBsTimedOut, cbstuff.GotSomeSpaces,
1525             cbstuff.DeleteAllCallBacks);
1526     fprintf(stderr, "%d CBs, %d FEs, (%d of total of %d 16-byte blocks)\n",
1527             cbstuff.nCBs, cbstuff.nFEs, cbstuff.nCBs+cbstuff.nFEs, cbstuff.nblks);
1528
1529 } /*PrintCallBackStats*/
1530
1531
1532 #define MAGIC 0x12345678    /* To check byte ordering of dump when it is read in */
1533
1534 #ifndef INTERPRET_DUMP
1535
1536
1537 DumpCallBackState()
1538
1539 {
1540     int fd;
1541     afs_uint32 magic = MAGIC, now = FT_ApproxTime(), freelisthead;
1542
1543     fd = open(AFSDIR_SERVER_CBKDUMP_FILEPATH, O_WRONLY|O_CREAT|O_TRUNC, 0666);
1544     if (fd < 0) {
1545         ViceLog(0, ("Couldn't create callback dump file %s\n", AFSDIR_SERVER_CBKDUMP_FILEPATH));
1546         return;
1547     }
1548     write(fd, &magic, sizeof(magic));
1549     write(fd, &now, sizeof(now));
1550     write(fd, &cbstuff, sizeof(cbstuff));
1551     write(fd, TimeOuts, sizeof(TimeOuts));
1552     write(fd, timeout, sizeof(timeout));
1553     write(fd, &tfirst, sizeof(tfirst));
1554     freelisthead = cbtoi((struct CallBack *) CBfree);
1555     write(fd, &freelisthead, sizeof(freelisthead)); /* This is a pointer */
1556     freelisthead = fetoi((struct FileEntry *) FEfree);
1557     write(fd, &freelisthead, sizeof(freelisthead)); /* This is a pointer */
1558     write(fd, HashTable, sizeof(HashTable));
1559     write(fd, &CB[1], sizeof(CB[1])*cbstuff.nblks); /* CB stuff */
1560     write(fd, &FE[1], sizeof(FE[1])*cbstuff.nblks); /* FE stuff */
1561     close(fd);
1562
1563 } /*DumpCallBackState*/
1564
1565 #endif
1566
1567 #ifdef INTERPRET_DUMP
1568
1569 /* This is only compiled in for the callback analyzer program */
1570 /* Returns the time of the dump */
1571 time_t ReadDump(file)
1572     char *file;
1573
1574 {
1575     int fd;
1576     afs_uint32 magic, freelisthead;
1577     time_t now;
1578
1579     fd = open(file, O_RDONLY);
1580     if (fd < 0) {
1581         fprintf(stderr, "Couldn't read dump file %s\n", file);
1582         exit(1);
1583     }
1584     read(fd, &magic, sizeof(magic));
1585     if (magic != MAGIC) {
1586         fprintf(stderr, "Magic number of %s is invalid.  You might be trying to\n",
1587                 file);
1588         fprintf(stderr,
1589                 "run this program on a machine type with a different byte ordering.\n");
1590         exit(1);
1591     }
1592     read(fd, &now, sizeof(now));
1593     read(fd, &cbstuff, sizeof(cbstuff));
1594     read(fd, TimeOuts, sizeof(TimeOuts));
1595     read(fd, timeout, sizeof(timeout));
1596     read(fd, &tfirst, sizeof(tfirst));
1597     read(fd, &freelisthead, sizeof(freelisthead));
1598     CB = ((struct CallBack *)(malloc(sizeof(struct FileEntry)*cbstuff.nblks)))-1;
1599     FE = ((struct FileEntry *)(malloc(sizeof(struct FileEntry)*cbstuff.nblks)))-1;
1600     CBfree = (struct CallBack *) itocb(freelisthead);
1601     read(fd, &freelisthead, sizeof(freelisthead));
1602     FEfree = (struct FileEntry *) itofe(freelisthead);
1603     read(fd, HashTable, sizeof(HashTable));
1604     read(fd, &CB[1], sizeof(CB[1])*cbstuff.nblks); /* CB stuff */
1605     read(fd, &FE[1], sizeof(FE[1])*cbstuff.nblks); /* FE stuff */
1606     if (close(fd)) {
1607         perror("Error reading dumpfile");
1608         exit(1);
1609     }
1610     return now;
1611
1612 } /*ReadDump*/
1613
1614
1615 #include "AFS_component_version_number.c"
1616
1617 main(argc, argv)
1618     int argc;
1619     char **argv;
1620
1621 {
1622     int err = 0, cbi = 0, stats = 0, noptions = 0, all = 0, vol = 0, raw = 0;
1623     static AFSFid fid;
1624     register struct FileEntry *fe;
1625     register struct CallBack *cb;
1626     time_t now;
1627
1628     bzero(&fid, sizeof(fid));
1629     argc--; argv++;
1630     while (argc && **argv == '-') {
1631         noptions++;
1632         argc--;
1633         if (!strcmp(*argv, "-host")) {
1634             if (argc < 1) {
1635                 err++;
1636                 break;
1637             }
1638             argc--;
1639             cbi = atoi(*++argv);
1640         }
1641         else if (!strcmp(*argv, "-fid")) {
1642             if (argc < 2) {
1643                 err++;
1644                 break;
1645             }
1646             argc -= 3;
1647             fid.Volume = atoi(*++argv);
1648             fid.Vnode = atoi(*++argv);
1649             fid.Unique = atoi(*++argv);
1650         }
1651         else if (!strcmp(*argv, "-time")) {
1652             fprintf(stderr, "-time not supported\n");
1653             exit(1);
1654         }
1655         else if (!strcmp(*argv, "-stats")) {
1656             stats = 1;
1657         }
1658         else if (!strcmp(*argv, "-all")) {
1659             all = 1;
1660         }
1661         else if (!strcmp(*argv, "-raw")) {
1662             raw = 1;
1663         }
1664         else if (!strcmp(*argv, "-volume")) {
1665             if (argc < 1) {
1666                 err++;
1667                 break;
1668             }
1669             argc--;
1670             vol = atoi(*++argv);
1671         }
1672         else err++;
1673         argv++;
1674     }
1675     if (err || argc != 1) {
1676         fprintf(stderr,
1677                 "Usage: cbd [-host cbid] [-fid volume vnode] [-stats] callbackdumpfile\n");
1678         fprintf(stderr, "[cbid is shown for each host in the hosts.dump file]\n");
1679         exit(1);
1680     }
1681     now = ReadDump(*argv);
1682     if (stats || noptions == 0) {
1683         time_t uxtfirst = UXtime(tfirst);
1684         printf("The time of the dump was %u %s", now, ctime(&now));
1685         printf("The last time cleanup ran was %u %s", uxtfirst, ctime(&uxtfirst));
1686         PrintCallBackStats();
1687     }
1688     if (all || vol) {
1689         register hash;
1690         register u_short *feip;
1691         register struct CallBack *cb;
1692         register struct FileEntry *fe;
1693
1694         for (hash=0; hash<VHASH; hash++) {
1695             for (feip = &HashTable[hash]; fe = itofe(*feip); ) {
1696                 if (!vol || (fe->volid == vol)) {
1697                     register struct CallBack *cbnext;
1698                     for (cb = itocb(fe->firstcb); cb; cb = cbnext) {
1699                         PrintCB(cb,now);
1700                         cbnext = itocb(cb->cnext);
1701                     }
1702                     *feip = fe->fnext;
1703                 } else {
1704                     feip = &fe->fnext;
1705                 }
1706             }
1707         }
1708     }
1709     if (cbi) {
1710         u_short cfirst = cbi;
1711         do {
1712             cb = itocb(cbi);
1713             PrintCB(cb,now);
1714             cbi = cb->hnext;
1715         } while (cbi != cfirst);
1716     }
1717     if (fid.Volume) {
1718         fe = FindFE(&fid);
1719         if (!fe) {
1720             printf("No callback entries for %u.%u\n", fid.Volume, fid.Vnode);
1721             exit(1);
1722         }
1723         cb = itocb(fe->firstcb);
1724         while (cb) {
1725             PrintCB(cb, now);
1726             cb = itocb(cb->cnext);
1727         }
1728     }
1729     if (raw) {
1730         struct FileEntry *fe;
1731         afs_int32 *p, i;
1732         for (i=1; i < cbstuff.nblks; i++) {
1733             p = (afs_int32 *) &FE[i];
1734             printf("%d:%12x%12x%12x%12x\n", i, p[0], p[1], p[2], p[3]);
1735         }
1736     }
1737 }
1738
1739 PrintCB(cb, now)
1740     register struct CallBack *cb;
1741     afs_uint32 now;
1742
1743 {
1744     struct FileEntry *fe = itofe(cb->fhead);
1745     time_t expires = TIndexToTime(cb->thead);
1746
1747     printf("vol=%u vn=%u cbs=%d hi=%d st=%d, exp in %d secs at %s", 
1748            fe->volid, fe->vnode, fe->ncbs, cb->hhead, cb->status,
1749            expires - now, ctime(&expires));
1750
1751 } /*PrintCB*/
1752
1753
1754 #endif
1755
1756 #if     !defined(INTERPRET_DUMP)
1757 /*
1758 ** try breaking calbacks on afidp from host. Use multi_rx.
1759 ** return 0 on success, non-zero on failure
1760 */
1761 int
1762 MultiBreakCallBackAlternateAddress(host, afidp)
1763 struct host*            host;
1764 struct AFSCBFids*       afidp;
1765 {
1766     int retVal;
1767     H_LOCK
1768     retVal = MultiBreakCallBackAlternateAddress_r(host, afidp);
1769     H_UNLOCK
1770     return retVal;
1771 }
1772
1773 int
1774 MultiBreakCallBackAlternateAddress_r(host, afidp)
1775 struct host*            host;
1776 struct AFSCBFids*       afidp;
1777 {
1778         int i,j;
1779         struct rx_connection*   conns[AFS_MAX_INTERFACE_ADDR];
1780         struct rx_connection*   connSuccess = 0;
1781         afs_int32                       addr[AFS_MAX_INTERFACE_ADDR];
1782         static struct rx_securityClass *sc = 0;
1783         static struct AFSCBs tc = {0,0};
1784
1785         /* nothing more can be done */
1786         if ( !host->interface ) return 1;       /* failure */
1787
1788         assert(host->interface->numberOfInterfaces > 0 );
1789
1790         /* the only address is the primary interface */
1791         if ( host->interface->numberOfInterfaces == 1 )
1792                 return 1;                       /* failure */
1793
1794         /* initialise a security object only once */
1795         if ( !sc )
1796             sc = (struct rx_securityClass *) rxnull_NewClientSecurityObject();
1797
1798         /* initialize alternate rx connections */
1799         for ( i=0,j=0; i < host->interface->numberOfInterfaces; i++)
1800         {
1801                 /* this is the current primary address */
1802                 if ( host->host == host->interface->addr[i] )
1803                         continue;       
1804
1805                 addr[j]    = host->interface->addr[i];
1806                 conns[j] =  rx_NewConnection (host->interface->addr[i],
1807                                 host->port, 1, sc, 0);
1808                 rx_SetConnDeadTime(conns[j], 2); 
1809                 rx_SetConnHardDeadTime(conns[j], AFS_HARDDEADTIME); 
1810                 j++;
1811         }
1812
1813         assert(j);  /* at least one alternate address */
1814         ViceLog(125,("Starting multibreakcall back on all addr for host:%x\n",
1815                         host->host));
1816         H_UNLOCK
1817         multi_Rx(conns, j)
1818         {
1819                 multi_RXAFSCB_CallBack(afidp, &tc);     
1820                 if ( !multi_error )
1821                 {
1822                         /* first success */
1823                         H_LOCK
1824                         if ( host->callback_rxcon )
1825                                 rx_DestroyConnection(host->callback_rxcon);
1826                         host->callback_rxcon = conns[multi_i];
1827                         host->host           = addr[multi_i];
1828                         connSuccess          = conns[multi_i];
1829                         rx_SetConnDeadTime(host->callback_rxcon, 50);
1830                         rx_SetConnHardDeadTime(host->callback_rxcon, AFS_HARDDEADTIME);
1831                         ViceLog(125,("multibreakcall success with addr:%x\n",
1832                                         addr[multi_i]));
1833                         H_UNLOCK
1834                         multi_Abort; 
1835                 }
1836         } multi_End_Ignore;
1837         H_LOCK
1838
1839         /* Destroy all connections except the one on which we succeeded */
1840         for ( i=0; i < j; i++)
1841                 if ( conns[i] != connSuccess )
1842                         rx_DestroyConnection(conns[i] );
1843
1844         if ( connSuccess ) return 0;    /* success */
1845                 else return 1;          /* failure */
1846 }
1847
1848
1849 /*
1850 ** try multiRX probes to host. 
1851 ** return 0 on success, non-zero on failure
1852 */
1853 int
1854 MultiProbeAlternateAddress_r(host)
1855 struct host*            host;
1856 {
1857         int i,j;
1858         struct rx_connection*   conns[AFS_MAX_INTERFACE_ADDR];
1859         struct rx_connection*   connSuccess = 0;
1860         afs_int32                       addr[AFS_MAX_INTERFACE_ADDR];
1861         static struct rx_securityClass *sc = 0;
1862
1863         /* nothing more can be done */
1864         if ( !host->interface ) return 1;       /* failure */
1865
1866         assert(host->interface->numberOfInterfaces > 0 );
1867
1868         /* the only address is the primary interface */
1869         if ( host->interface->numberOfInterfaces == 1 )
1870                 return 1;                       /* failure */
1871
1872         /* initialise a security object only once */
1873         if ( !sc )
1874             sc = (struct rx_securityClass *) rxnull_NewClientSecurityObject();
1875
1876         /* initialize alternate rx connections */
1877         for ( i=0,j=0; i < host->interface->numberOfInterfaces; i++)
1878         {
1879                 /* this is the current primary address */
1880                 if ( host->host == host->interface->addr[i] )
1881                         continue;       
1882
1883                 addr[j]    = host->interface->addr[i];
1884                 conns[j] =  rx_NewConnection (host->interface->addr[i],
1885                                 host->port, 1, sc, 0);
1886                 rx_SetConnDeadTime(conns[j], 2); 
1887                 rx_SetConnHardDeadTime(conns[j], AFS_HARDDEADTIME); 
1888                 j++;
1889         }
1890
1891         assert(j);  /* at least one alternate address */
1892         ViceLog(125,("Starting multiprobe on all addr for host:%x\n",
1893                         host->host));
1894         H_UNLOCK
1895         multi_Rx(conns, j)
1896         {
1897                 multi_RXAFSCB_ProbeUuid(&host->interface->uuid);        
1898                 if ( !multi_error )
1899                 {
1900                         /* first success */
1901                         H_LOCK
1902                         if ( host->callback_rxcon )
1903                                 rx_DestroyConnection(host->callback_rxcon);
1904                         host->callback_rxcon = conns[multi_i];
1905                         host->host           = addr[multi_i];
1906                         connSuccess          = conns[multi_i];
1907                         rx_SetConnDeadTime(host->callback_rxcon, 50);
1908                         rx_SetConnHardDeadTime(host->callback_rxcon, AFS_HARDDEADTIME);
1909                         ViceLog(125,("multiprobe success with addr:%x\n",
1910                                         addr[multi_i]));
1911                         H_UNLOCK
1912                         multi_Abort; 
1913                 }
1914         } multi_End_Ignore;
1915         H_LOCK
1916
1917         /* Destroy all connections except the one on which we succeeded */
1918         for ( i=0; i < j; i++)
1919                 if ( conns[i] != connSuccess )
1920                         rx_DestroyConnection(conns[i] );
1921
1922         if ( connSuccess ) return 0;    /* success */
1923                 else return 1;          /* failure */
1924 }
1925
1926 #endif /* !defined(INTERPRET_DUMP) */