viced-rewrite-breaklatercallbacks-20030218
[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         ShutDown();
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           ShutDown();
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           ShutDown();
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         (struct object *)fe = ((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                   ShutDown();
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 int ClearHostCallbacks(struct host *hp, int locked)
1546 {
1547     int retVal;
1548     H_LOCK
1549     retVal = ClearHostCallbacks_r(hp, locked);
1550     H_UNLOCK
1551     return retVal;
1552 }
1553
1554 /* locked - set if caller has already locked the host */
1555 int ClearHostCallbacks_r(struct host *hp, int locked)
1556 {
1557     struct interfaceAddr interf;
1558     int code;
1559     int held = 0;
1560     char hoststr[16];
1561
1562     ViceLog(5,("GSS: Delete longest inactive host %s\n", afs_inet_ntoa_r(hp->host,hoststr)));
1563     if ( !(held = h_Held_r(hp)) )
1564         h_Hold_r(hp);
1565
1566     /** Try a non-blocking lock. If the lock is already held return
1567       * after releasing hold on hp
1568       */
1569     if (!locked) {
1570        if ( h_NBLock_r(hp) ) {
1571            if ( !held )
1572                h_Release_r(hp);
1573            return 1;
1574        }
1575     }
1576     if (hp->Console & 2) {
1577         /*
1578          * If the special console field is set it means that a thread
1579          * is waiting in AddCallBack1 after it set pointers to the
1580          * file entry and/or callback entry. Because of the bogus
1581          * usage of h_hold it won't prevent from another thread, this
1582          * one, to remove all the callbacks so just to be safe we keep
1583          * a reference. NOTE, on the last phase we'll free the calling
1584          * host's callbacks but that's ok...
1585          */
1586         cbstuff.GSS5++;
1587     }
1588     DeleteAllCallBacks_r(hp, 1);
1589     if (hp->hostFlags & VENUSDOWN) {
1590         hp->hostFlags &= ~RESETDONE;    /* remember that we must do a reset */
1591     } else {
1592         /* host is up, try a call */
1593         hp->hostFlags &= ~ALTADDR; /* alternate addresses are invalid */
1594         if (hp->interface) {
1595             H_UNLOCK
1596             code = RXAFSCB_InitCallBackState3(hp->callback_rxcon, &FS_HostUUID);
1597             H_LOCK
1598         } else {
1599             H_UNLOCK
1600             code = RXAFSCB_InitCallBackState(hp->callback_rxcon);
1601             H_LOCK
1602         }
1603         hp->hostFlags |= ALTADDR; /* alternate addresses are valid */
1604         if (code)
1605         {
1606             /* failed, mark host down and need reset */
1607             hp->hostFlags |= VENUSDOWN;
1608             hp->hostFlags &= ~RESETDONE;
1609         } else {
1610             /* reset succeeded, we're done */
1611             hp->hostFlags |= RESETDONE;
1612         }
1613     }
1614     if (!locked) {
1615        h_Unlock_r(hp);
1616     }
1617     if ( !held )
1618         h_Release_r(hp);
1619
1620     return 0;
1621 }
1622 #endif /* INTERPRET_DUMP */
1623
1624
1625 int PrintCallBackStats(void)
1626 {
1627     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",
1628             cbstuff.AddCallBacks, cbstuff.BreakCallBacks, cbstuff.DeleteCallBacks,
1629             cbstuff.DeleteFiles, cbstuff.CBsTimedOut, cbstuff.GotSomeSpaces,
1630             cbstuff.DeleteAllCallBacks);
1631     fprintf(stderr, "%d CBs, %d FEs, (%d of total of %d 16-byte blocks)\n",
1632             cbstuff.nCBs, cbstuff.nFEs, cbstuff.nCBs+cbstuff.nFEs, cbstuff.nblks);
1633
1634     return 0;
1635 }
1636
1637 #define MAGIC 0x12345678    /* To check byte ordering of dump when it is read in */
1638
1639 #ifndef INTERPRET_DUMP
1640
1641 int DumpCallBackState(void)
1642 {
1643     int fd;
1644     afs_uint32 magic = MAGIC, now = FT_ApproxTime(), freelisthead;
1645
1646     fd = open(AFSDIR_SERVER_CBKDUMP_FILEPATH, O_WRONLY|O_CREAT|O_TRUNC, 0666);
1647     if (fd < 0) {
1648         ViceLog(0, ("Couldn't create callback dump file %s\n", AFSDIR_SERVER_CBKDUMP_FILEPATH));
1649         return 0;
1650     }
1651     write(fd, &magic, sizeof(magic));
1652     write(fd, &now, sizeof(now));
1653     write(fd, &cbstuff, sizeof(cbstuff));
1654     write(fd, TimeOuts, sizeof(TimeOuts));
1655     write(fd, timeout, sizeof(timeout));
1656     write(fd, &tfirst, sizeof(tfirst));
1657     freelisthead = cbtoi((struct CallBack *) CBfree);
1658     write(fd, &freelisthead, sizeof(freelisthead)); /* This is a pointer */
1659     freelisthead = fetoi((struct FileEntry *) FEfree);
1660     write(fd, &freelisthead, sizeof(freelisthead)); /* This is a pointer */
1661     write(fd, HashTable, sizeof(HashTable));
1662     write(fd, &CB[1], sizeof(CB[1])*cbstuff.nblks); /* CB stuff */
1663     write(fd, &FE[1], sizeof(FE[1])*cbstuff.nblks); /* FE stuff */
1664     close(fd);
1665
1666     return 0;
1667 }
1668
1669 #endif
1670
1671 #ifdef INTERPRET_DUMP
1672
1673 /* This is only compiled in for the callback analyzer program */
1674 /* Returns the time of the dump */
1675 time_t ReadDump(char *file)
1676 {
1677     int fd;
1678     afs_uint32 magic, freelisthead;
1679     time_t now;
1680
1681     fd = open(file, O_RDONLY);
1682     if (fd < 0) {
1683         fprintf(stderr, "Couldn't read dump file %s\n", file);
1684         exit(1);
1685     }
1686     read(fd, &magic, sizeof(magic));
1687     if (magic != MAGIC) {
1688         fprintf(stderr, "Magic number of %s is invalid.  You might be trying to\n",
1689                 file);
1690         fprintf(stderr,
1691                 "run this program on a machine type with a different byte ordering.\n");
1692         exit(1);
1693     }
1694     read(fd, &now, sizeof(now));
1695     read(fd, &cbstuff, sizeof(cbstuff));
1696     read(fd, TimeOuts, sizeof(TimeOuts));
1697     read(fd, timeout, sizeof(timeout));
1698     read(fd, &tfirst, sizeof(tfirst));
1699     read(fd, &freelisthead, sizeof(freelisthead));
1700     CB = ((struct CallBack *)(malloc(sizeof(struct FileEntry)*cbstuff.nblks)))-1;
1701     FE = ((struct FileEntry *)(malloc(sizeof(struct FileEntry)*cbstuff.nblks)))-1;
1702     CBfree = (struct CallBack *) itocb(freelisthead);
1703     read(fd, &freelisthead, sizeof(freelisthead));
1704     FEfree = (struct FileEntry *) itofe(freelisthead);
1705     read(fd, HashTable, sizeof(HashTable));
1706     read(fd, &CB[1], sizeof(CB[1])*cbstuff.nblks); /* CB stuff */
1707     read(fd, &FE[1], sizeof(FE[1])*cbstuff.nblks); /* FE stuff */
1708     if (close(fd)) {
1709         perror("Error reading dumpfile");
1710         exit(1);
1711     }
1712     return now;
1713 }
1714
1715 #include "AFS_component_version_number.c"
1716
1717 int main(int argc, char **argv)
1718 {
1719     int err = 0, cbi = 0, stats = 0, noptions = 0, all = 0, vol = 0, raw = 0;
1720     static AFSFid fid;
1721     register struct FileEntry *fe;
1722     register struct CallBack *cb;
1723     time_t now;
1724
1725     memset(&fid, 0, sizeof(fid));
1726     argc--; argv++;
1727     while (argc && **argv == '-') {
1728         noptions++;
1729         argc--;
1730         if (!strcmp(*argv, "-host")) {
1731             if (argc < 1) {
1732                 err++;
1733                 break;
1734             }
1735             argc--;
1736             cbi = atoi(*++argv);
1737         }
1738         else if (!strcmp(*argv, "-fid")) {
1739             if (argc < 2) {
1740                 err++;
1741                 break;
1742             }
1743             argc -= 3;
1744             fid.Volume = atoi(*++argv);
1745             fid.Vnode = atoi(*++argv);
1746             fid.Unique = atoi(*++argv);
1747         }
1748         else if (!strcmp(*argv, "-time")) {
1749             fprintf(stderr, "-time not supported\n");
1750             exit(1);
1751         }
1752         else if (!strcmp(*argv, "-stats")) {
1753             stats = 1;
1754         }
1755         else if (!strcmp(*argv, "-all")) {
1756             all = 1;
1757         }
1758         else if (!strcmp(*argv, "-raw")) {
1759             raw = 1;
1760         }
1761         else if (!strcmp(*argv, "-volume")) {
1762             if (argc < 1) {
1763                 err++;
1764                 break;
1765             }
1766             argc--;
1767             vol = atoi(*++argv);
1768         }
1769         else err++;
1770         argv++;
1771     }
1772     if (err || argc != 1) {
1773         fprintf(stderr,
1774                 "Usage: cbd [-host cbid] [-fid volume vnode] [-stats] callbackdumpfile\n");
1775         fprintf(stderr, "[cbid is shown for each host in the hosts.dump file]\n");
1776         exit(1);
1777     }
1778     now = ReadDump(*argv);
1779     if (stats || noptions == 0) {
1780         time_t uxtfirst = UXtime(tfirst);
1781         printf("The time of the dump was %u %s", now, ctime(&now));
1782         printf("The last time cleanup ran was %u %s", uxtfirst, ctime(&uxtfirst));
1783         PrintCallBackStats();
1784     }
1785     if (all || vol) {
1786         register hash;
1787         register afs_uint32 *feip;
1788         register struct CallBack *cb;
1789         register struct FileEntry *fe;
1790
1791         for (hash=0; hash<VHASH; hash++) {
1792             for (feip = &HashTable[hash]; fe = itofe(*feip); ) {
1793                 if (!vol || (fe->volid == vol)) {
1794                     register struct CallBack *cbnext;
1795                     for (cb = itocb(fe->firstcb); cb; cb = cbnext) {
1796                         PrintCB(cb,now);
1797                         cbnext = itocb(cb->cnext);
1798                     }
1799                     *feip = fe->fnext;
1800                 } else {
1801                     feip = &fe->fnext;
1802                 }
1803             }
1804         }
1805     }
1806     if (cbi) {
1807         afs_uint32 cfirst = cbi;
1808         do {
1809             cb = itocb(cbi);
1810             PrintCB(cb,now);
1811             cbi = cb->hnext;
1812         } while (cbi != cfirst);
1813     }
1814     if (fid.Volume) {
1815         fe = FindFE(&fid);
1816         if (!fe) {
1817             printf("No callback entries for %u.%u\n", fid.Volume, fid.Vnode);
1818             exit(1);
1819         }
1820         cb = itocb(fe->firstcb);
1821         while (cb) {
1822             PrintCB(cb, now);
1823             cb = itocb(cb->cnext);
1824         }
1825     }
1826     if (raw) {
1827         struct FileEntry *fe;
1828         afs_int32 *p, i;
1829         for (i=1; i < cbstuff.nblks; i++) {
1830             p = (afs_int32 *) &FE[i];
1831             printf("%d:%12x%12x%12x%12x\n", i, p[0], p[1], p[2], p[3]);
1832         }
1833     }
1834 }
1835
1836 int PrintCB(register struct CallBack *cb, afs_uint32 now)
1837 {
1838     struct FileEntry *fe = itofe(cb->fhead);
1839     time_t expires = TIndexToTime(cb->thead);
1840
1841     printf("vol=%u vn=%u cbs=%d hi=%d st=%d fest=%d, exp in %d secs at %s", 
1842            fe->volid, fe->vnode, fe->ncbs, cb->hhead, cb->status,
1843            fe->status, expires - now, ctime(&expires));
1844 }
1845
1846 #endif
1847
1848 #if     !defined(INTERPRET_DUMP)
1849 /*
1850 ** try breaking calbacks on afidp from host. Use multi_rx.
1851 ** return 0 on success, non-zero on failure
1852 */
1853 int MultiBreakCallBackAlternateAddress(struct host *host, struct AFSCBFids *afidp)
1854 {
1855     int retVal;
1856     H_LOCK
1857     retVal = MultiBreakCallBackAlternateAddress_r(host, afidp);
1858     H_UNLOCK
1859     return retVal;
1860 }
1861
1862 int MultiBreakCallBackAlternateAddress_r(struct host *host, struct AFSCBFids *afidp)
1863 {
1864         int i,j;
1865         struct rx_connection**  conns;
1866         struct rx_connection*   connSuccess = 0;
1867         afs_int32               *addr;
1868         static struct rx_securityClass *sc = 0;
1869         static struct AFSCBs tc = {0,0};
1870         char hoststr[16];
1871
1872         /* nothing more can be done */
1873         if ( !host->interface ) return 1;       /* failure */
1874
1875         assert(host->interface->numberOfInterfaces > 0 );
1876
1877         /* the only address is the primary interface */
1878         if ( host->interface->numberOfInterfaces == 1 )
1879                 return 1;                       /* failure */
1880
1881         /* initialise a security object only once */
1882         if ( !sc )
1883             sc = rxnull_NewClientSecurityObject();
1884
1885         i = host->interface->numberOfInterfaces;
1886         addr = malloc(i * sizeof(afs_int32));
1887         conns = malloc(i * sizeof(struct rx_connection *));
1888         if (!addr || !conns) {
1889             ViceLog(0, ("Failed malloc in MultiBreakCallBackAlternateAddress_r\n"));
1890             assert(0);
1891         }
1892
1893         /* initialize alternate rx connections */
1894         for ( i=0,j=0; i < host->interface->numberOfInterfaces; i++)
1895         {
1896                 /* this is the current primary address */
1897                 if ( host->host == host->interface->addr[i] )
1898                         continue;       
1899
1900                 addr[j]    = host->interface->addr[i];
1901                 conns[j] =  rx_NewConnection (host->interface->addr[i],
1902                                 host->port, 1, sc, 0);
1903                 rx_SetConnDeadTime(conns[j], 2); 
1904                 rx_SetConnHardDeadTime(conns[j], AFS_HARDDEADTIME); 
1905                 j++;
1906         }
1907
1908         assert(j);  /* at least one alternate address */
1909         ViceLog(125,("Starting multibreakcall back on all addr for host %s\n",
1910                         afs_inet_ntoa_r(host->host,hoststr)));
1911         H_UNLOCK
1912         multi_Rx(conns, j)
1913         {
1914                 multi_RXAFSCB_CallBack(afidp, &tc);     
1915                 if ( !multi_error )
1916                 {
1917                         /* first success */
1918                         H_LOCK
1919                         if ( host->callback_rxcon )
1920                                 rx_DestroyConnection(host->callback_rxcon);
1921                         host->callback_rxcon = conns[multi_i];
1922                         host->host           = addr[multi_i];
1923                         connSuccess          = conns[multi_i];
1924                         rx_SetConnDeadTime(host->callback_rxcon, 50);
1925                         rx_SetConnHardDeadTime(host->callback_rxcon, AFS_HARDDEADTIME);
1926                         ViceLog(125,("multibreakcall success with addr %s\n",
1927                                         afs_inet_ntoa_r(addr[multi_i],hoststr)));
1928                         H_UNLOCK
1929                         multi_Abort; 
1930                 }
1931         } multi_End_Ignore;
1932         H_LOCK
1933
1934         /* Destroy all connections except the one on which we succeeded */
1935         for ( i=0; i < j; i++)
1936                 if ( conns[i] != connSuccess )
1937                         rx_DestroyConnection(conns[i] );
1938
1939         free(addr);
1940         free(conns);
1941
1942         if ( connSuccess ) return 0;    /* success */
1943                 else return 1;          /* failure */
1944 }
1945
1946
1947 /*
1948 ** try multiRX probes to host. 
1949 ** return 0 on success, non-zero on failure
1950 */
1951 int MultiProbeAlternateAddress_r(struct host *host)
1952 {
1953         int i,j;
1954         struct rx_connection**  conns;
1955         struct rx_connection*   connSuccess = 0;
1956         afs_int32               *addr;
1957         static struct rx_securityClass *sc = 0;
1958         char hoststr[16];
1959
1960         /* nothing more can be done */
1961         if ( !host->interface ) return 1;       /* failure */
1962
1963         assert(host->interface->numberOfInterfaces > 0 );
1964
1965         /* the only address is the primary interface */
1966         if ( host->interface->numberOfInterfaces == 1 )
1967                 return 1;                       /* failure */
1968
1969         /* initialise a security object only once */
1970         if ( !sc )
1971             sc = rxnull_NewClientSecurityObject();
1972
1973         i = host->interface->numberOfInterfaces;
1974         addr = malloc(i * sizeof(afs_int32));   
1975         conns = malloc(i * sizeof(struct rx_connection *));
1976         if (!addr || !conns) {
1977             ViceLog(0, ("Failed malloc in MultiProbeAlternateAddress_r\n"));
1978             assert(0);
1979         }
1980
1981         /* initialize alternate rx connections */
1982         for ( i=0,j=0; i < host->interface->numberOfInterfaces; i++)
1983         {
1984                 /* this is the current primary address */
1985                 if ( host->host == host->interface->addr[i] )
1986                         continue;       
1987
1988                 addr[j]    = host->interface->addr[i];
1989                 conns[j] =  rx_NewConnection (host->interface->addr[i],
1990                                 host->port, 1, sc, 0);
1991                 rx_SetConnDeadTime(conns[j], 2); 
1992                 rx_SetConnHardDeadTime(conns[j], AFS_HARDDEADTIME); 
1993                 j++;
1994         }
1995
1996         assert(j);  /* at least one alternate address */
1997         ViceLog(125,("Starting multiprobe on all addr for host %s\n",
1998                         afs_inet_ntoa_r(host->host,hoststr)));
1999         H_UNLOCK
2000         multi_Rx(conns, j)
2001         {
2002                 multi_RXAFSCB_ProbeUuid(&host->interface->uuid);        
2003                 if ( !multi_error )
2004                 {
2005                         /* first success */
2006                         H_LOCK
2007                         if ( host->callback_rxcon )
2008                                 rx_DestroyConnection(host->callback_rxcon);
2009                         host->callback_rxcon = conns[multi_i];
2010                         host->host           = addr[multi_i];
2011                         connSuccess          = conns[multi_i];
2012                         rx_SetConnDeadTime(host->callback_rxcon, 50);
2013                         rx_SetConnHardDeadTime(host->callback_rxcon, AFS_HARDDEADTIME);
2014                         ViceLog(125,("multiprobe success with addr %s\n",
2015                                         afs_inet_ntoa_r(addr[multi_i],hoststr)));
2016                         H_UNLOCK
2017                         multi_Abort; 
2018                 }
2019         } multi_End_Ignore;
2020         H_LOCK
2021
2022         /* Destroy all connections except the one on which we succeeded */
2023         for ( i=0; i < j; i++)
2024                 if ( conns[i] != connSuccess )
2025                         rx_DestroyConnection(conns[i] );
2026
2027         free(addr);
2028         free(conns);
2029
2030         if ( connSuccess ) return 0;    /* success */
2031                 else return 1;          /* failure */
2032 }
2033
2034 #endif /* !defined(INTERPRET_DUMP) */