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