windows-give-up-callbacks-20070627
authorJeffrey Altman <jaltman@secure-endpoints.com>
Thu, 28 Jun 2007 05:04:21 +0000 (05:04 +0000)
committerJeffrey Altman <jaltman@secure-endpoints.com>
Thu, 28 Jun 2007 05:04:21 +0000 (05:04 +0000)
This large patch adds support for giving up callbacks in response to three
events:

 1. power management suspend

 2. power management shutdown

 3. stat cache object recycling

The third item is submitted as a condition compilation if GIVE_UP_CALLBACKS
is defined.  Properly handing callback give ups and the associated race
conditions with revokes and fetch status requests requires a great deal of
over head.  The first attempt used one GiveUpCallBacks RPC for each callback
that was being dropped as the stat cache object was recycled.  This resulted
in a 27% performance drop in the MIT stress test.  The code that is being
committed maintains a callback give up list on each server object.  The
callback is added to the list as the callbacks are dropped and then they
are sent to the server in bulk by the background daemon thread if the
server is known to be UP after a ping.  Logic is added to the
EndCallbackRequest and CallbackRevoke operations to ensure that race
conditions are addressed.  With all of this, there is a 17% performance drop
in the MIT stress test.

As a result, it is my conclusion that the client side costs associated with
optimizing the load on the server are simply too high.  I am committing this
code to ensure that it is not lost.  I will remove this support in the next
patch while leaving the support for giving up all callbacks in response
to suspend and shutdown events.

src/WINNT/afsd/afsd_service.c
src/WINNT/afsd/cm_callback.c
src/WINNT/afsd/cm_callback.h
src/WINNT/afsd/cm_scache.c
src/WINNT/afsd/cm_scache.h
src/WINNT/afsd/cm_server.c
src/WINNT/afsd/cm_server.h

index 9b0fad4..20fc69d 100644 (file)
@@ -311,16 +311,20 @@ afsd_ServiceControlHandlerEx(
                 case PBT_APMQUERYSUSPEND:       
                     afsi_log("SERVICE_CONTROL_APMQUERYSUSPEND"); 
                     /* Write all dirty buffers back to server */
-                   if ( !lana_OnlyLoopback() )
+                   if ( !lana_OnlyLoopback() ) {
                        buf_CleanAndReset();
+                        cm_SuspendSCache();
+                    }
                     afsi_log("SERVICE_CONTROL_APMQUERYSUSPEND buf_CleanAndReset complete"); 
                     dwRet = NO_ERROR;                       
                     break;                                  
                 case PBT_APMQUERYSTANDBY:                                                         
                     afsi_log("SERVICE_CONTROL_APMQUERYSTANDBY"); 
                     /* Write all dirty buffers back to server */
-                   if ( !lana_OnlyLoopback() ) 
+                   if ( !lana_OnlyLoopback() ) {
                        buf_CleanAndReset();
+                        cm_SuspendSCache();
+                    }
                     afsi_log("SERVICE_CONTROL_APMQUERYSTANDBY buf_CleanAndReset complete"); 
                     dwRet = NO_ERROR;                                                             
                     break;                                                                        
@@ -329,15 +333,19 @@ afsd_ServiceControlHandlerEx(
                 case PBT_APMSUSPEND:                         
                     afsi_log("SERVICE_CONTROL_APMSUSPEND");
                    powerStateSuspended = 1;
-                   if (osVersion.dwMajorVersion >= 6)
+                   if (osVersion.dwMajorVersion >= 6) {
+                        cm_SuspendSCache();
                        smb_StopListeners();
+                    }
                     dwRet = NO_ERROR;                       
                     break;                                  
                 case PBT_APMSTANDBY:                  
                     afsi_log("SERVICE_CONTROL_APMSTANDBY"); 
                    powerStateSuspended = 1;
-                   if (osVersion.dwMajorVersion >= 6)
+                   if (osVersion.dwMajorVersion >= 6) {
+                        cm_SuspendSCache();
                        smb_StopListeners();
+                    }
                     dwRet = NO_ERROR;                       
                     break;                                  
                 case PBT_APMRESUMECRITICAL:             
index 1b694ec..80bf705 100644 (file)
@@ -203,10 +203,19 @@ void cm_RevokeCallback(struct rx_call *callp, cm_cell_t * cellp, AFSFid *fidp)
             lock_ReleaseWrite(&cm_scacheLock);
             osi_Log4(afsd_logp, "RevokeCallback Discarding SCache scp 0x%p vol %u vn %u uniq %u", 
                      scp, scp->fid.volume, scp->fid.vnode, scp->fid.unique);
+
+#ifdef GIVE_UP_CALLBACKS
+            lock_ObtainMutex(&scp->cbServerp->mx);
+            cm_RemoveFidFromGiveUpCallBackList(scp->cbServerp, &scp->fid);
+            lock_ReleaseMutex(&scp->cbServerp->mx);
+#endif /* GIVE_UP_CALLBACKS */
+
             lock_ObtainMutex(&scp->mx);
             cm_DiscardSCache(scp);
             lock_ReleaseMutex(&scp->mx);
+
             cm_CallbackNotifyChange(scp);
+            
             lock_ObtainWrite(&cm_scacheLock);
             cm_ReleaseSCacheNoLock(scp);
         }
@@ -239,7 +248,6 @@ void cm_RevokeVolumeCallback(struct rx_call *callp, cm_cell_t *cellp, AFSFid *fi
     tfid.volume = fidp->Volume;
     cm_RecordRacingRevoke(&tfid, CM_RACINGFLAG_CANCELVOL);
 
-
     lock_ObtainWrite(&cm_scacheLock);
     for (hash = 0; hash < cm_data.scacheHashTableSize; hash++) {
         for(scp=cm_data.scacheHashTablep[hash]; scp; scp=scp->nextp) {
@@ -249,11 +257,19 @@ void cm_RevokeVolumeCallback(struct rx_call *callp, cm_cell_t *cellp, AFSFid *fi
                  scp->cbServerp != NULL) {
                 cm_HoldSCacheNoLock(scp);
                 lock_ReleaseWrite(&cm_scacheLock);
+
+#ifdef GIVE_UP_CALLBACKS
+                lock_ObtainMutex(&scp->cbServerp->mx);
+                cm_RemoveFidFromGiveUpCallBackList(scp->cbServerp, &scp->fid);
+                lock_ReleaseMutex(&scp->cbServerp->mx);
+#endif /* GIVE_UP_CALLBACKS */
+
                 lock_ObtainMutex(&scp->mx);
                 osi_Log4(afsd_logp, "RevokeVolumeCallback Discarding SCache scp 0x%p vol %u vn %u uniq %u", 
                           scp, scp->fid.volume, scp->fid.vnode, scp->fid.unique);
                 cm_DiscardSCache(scp);
                 lock_ReleaseMutex(&scp->mx);
+
                 cm_CallbackNotifyChange(scp);
                 lock_ObtainWrite(&cm_scacheLock);
                 cm_ReleaseSCacheNoLock(scp);
@@ -350,7 +366,7 @@ SRXAFSCB_CallBack(struct rx_call *callp, AFSCBFids *fidsArrayp, AFSCBs *cbsArray
         host = rx_HostOf(peerp);
         port = rx_PortOf(peerp);
 
-        tsp = cm_FindServerByIP(host);
+        tsp = cm_FindServerByIP(host, CM_SERVER_FILE);
         if (tsp)
             cellp = tsp->cellp;
     }
@@ -484,9 +500,24 @@ SRXAFSCB_InitCallBackState(struct rx_call *callp)
            cm_SetServerNo64Bit(tsp, 0);
            cm_SetServerNoInlineBulk(tsp, 0);
 
+#ifdef GIVE_UP_CALLBACKS
+            /* Clear the callbacks list */
+            lock_ObtainMutex(&tsp->mx);
+            cm_FreeGiveUpCallBackList(tsp);
+            lock_ReleaseMutex(&tsp->mx);
+#endif /* GIVE_UP_CALLBACKS */
+
            /* we're done with the server structure */
             cm_PutServer(tsp);
-       }
+       } 
+#ifdef GIVE_UP_CALLBACKS        
+        else {
+            /* if we didn't recognize the server, we cleared all callbacks 
+             * on all stat scache objects.  So we must clear all of the 
+             * Give Up CallBack lists */
+            cm_FreeAllGiveUpCallBackLists();
+        }
+#endif /* GIVE_UP_CALLBACKS */
     }
     MUTEX_EXIT(&callp->lock);
     return 0;
@@ -1526,8 +1557,11 @@ void cm_EndCallbackGrantingCall(cm_scache_t *scp, cm_callbackRequest_t *cbrp,
     cm_racingRevokes_t *revp;          /* where we are */
     cm_racingRevokes_t *nrevp;         /* where we'll be next */
     int freeFlag;
-    cm_server_t * serverp = 0;
+    cm_server_t * serverp = NULL;
     int discardScp = 0;
+#ifdef GIVE_UP_CALLBACKS
+    int addFidToGUCB = 0;
+#endif /* GIVE_UP_CALLBACKS */
 
     lock_ObtainWrite(&cm_callbackLock);
     if (flags & CM_CALLBACK_MAINTAINCOUNT) {
@@ -1546,6 +1580,9 @@ void cm_EndCallbackGrantingCall(cm_scache_t *scp, cm_callbackRequest_t *cbrp,
        if (scp) {
             if (scp->cbServerp != cbrp->serverp) {
                 serverp = scp->cbServerp;
+#ifdef GIVE_UP_CALLBACKS
+                addFidToGUCB = 1;
+#endif /* GIVE_UP_CALLBACKS */
                 if (!freeFlag)
                     cm_GetServer(cbrp->serverp);
                 scp->cbServerp = cbrp->serverp;
@@ -1607,9 +1644,23 @@ void cm_EndCallbackGrantingCall(cm_scache_t *scp, cm_callbackRequest_t *cbrp,
         lock_ReleaseMutex(&scp->mx);
         cm_CallbackNotifyChange(scp);
         lock_ObtainMutex(&scp->mx);
+    } 
+#ifdef GIVE_UP_CALLBACKS    
+    else if (scp) {
+        lock_ObtainMutex(&scp->cbServerp->mx);
+        cm_RemoveFidFromGiveUpCallBackList(scp->cbServerp, &scp->fid);
+        lock_ReleaseMutex(&scp->cbServerp->mx);
     }
+#endif /* GIVE_UP_CALLBACKS */
 
     if ( serverp ) {
+#ifdef GIVE_UP_CALLBACKS
+        if (addFidToGUCB) {
+            lock_ObtainMutex(&serverp->mx);
+            cm_AddFidToGiveUpCallBackList(serverp, &scp->fid);
+            lock_ReleaseMutex(&serverp->mx);
+        }
+#endif /* GIVE_UP_CALLBACKS */
         lock_ObtainWrite(&cm_serverLock);
         cm_FreeServer(serverp);
         lock_ReleaseWrite(&cm_serverLock);
@@ -1780,3 +1831,134 @@ void cm_CheckCBExpiration(void)
     osi_Log0(afsd_logp, "CheckCBExpiration Complete");
 }
 
+
+#ifdef GIVE_UP_CALLBACKS
+/* server mutex must be held */
+/* do not hold cm_scacheLock */
+void cm_GiveUpCallBacksToServer(cm_server_t * serverp)
+{
+    struct AFSFid *tfids = NULL;
+    struct AFSCallBack callBacks[1];
+    struct AFSCBFids fidArray;
+    struct AFSCBs cbArray;
+    afs_int32 code = 0;
+    cm_server_gucb_t *gucbp, *nextp;
+    cm_conn_t *connp;
+    struct rx_connection * callp;
+    cm_req_t req;
+    int i, j;
+
+    for ( gucbp = serverp->gucbs, serverp->gucbs = NULL, lock_ReleaseMutex(&serverp->mx); 
+          gucbp; 
+          gucbp = nextp ) 
+    {
+        nextp = gucbp->nextp;
+
+        if (!tfids) {   /* only need to do these once */
+            tfids = (struct AFSFid *)malloc(AFS_MAXCBRSCALL * sizeof(*tfids));
+            osi_Assert(tfids);
+
+            fidArray.AFSCBFids_val = (struct AFSFid *)tfids;
+
+            cbArray.AFSCBs_len = 1;
+            cbArray.AFSCBs_val = callBacks;
+            memset(&callBacks[0], 0, sizeof(callBacks[0]));
+            callBacks[0].CallBackType = CB_EXCLUSIVE;
+        }
+        memset(tfids, 0, AFS_MAXCBRSCALL * sizeof(*tfids));
+
+        for ( i=0, j=0; i<gucbp->count; i++ ) {
+            if (gucbp->fids[i].cell != 0) {
+                cm_AFSFidFromFid(&tfids[j++], &gucbp->fids[i]);
+            }
+        }
+        fidArray.AFSCBFids_len = j;
+
+        cm_InitReq(&req);
+        req.flags |= CM_REQ_NORETRY;
+
+        osi_Log1(afsd_logp, "CALL GiveUpCallbacks serverp %xp", serverp);
+        do {
+            code = cm_ConnByServer(serverp, cm_rootUserp, &connp);
+            if (code) 
+                continue;
+
+            callp = cm_GetRxConn(connp);
+            code = RXAFS_GiveUpCallBacks(callp, &fidArray, &cbArray);
+            rx_PutConnection(callp);
+        } while (cm_Analyze(connp, cm_rootUserp, &req, NULL, NULL, NULL, NULL, code));
+        code = cm_MapRPCError(code, &req);
+
+        if (code)
+            osi_Log1(afsd_logp, "CALL GiveUpCallback FAILURE, code 0x%x", code);
+        else
+            osi_Log0(afsd_logp, "CALL GiveUpCallback SUCCESS");
+
+        free(gucbp);
+    }
+    lock_ObtainMutex(&serverp->mx);
+}
+
+
+void
+cm_GiveUpCallback(cm_scache_t * scp)
+{
+    time_t now;
+
+    cm_HoldSCacheNoLock(scp);
+    now = osi_Time();
+
+    if (scp->cbExpires > 0 && (scp->cbServerp == NULL || now < scp->cbExpires)) {
+        lock_ObtainMutex(&scp->cbServerp->mx);
+        cm_AddFidToGiveUpCallBackList(scp->cbServerp, &scp->fid);
+        lock_ReleaseMutex(&scp->cbServerp->mx);
+
+        /* assume the callbacks were given up even if we fail */
+        cm_PutServer(scp->cbServerp);
+        scp->cbServerp = NULL;
+        scp->cbExpires = 0;
+    }
+    cm_ReleaseSCacheNoLock(scp);
+}
+#endif /* GIVE_UP_CALLBACKS */
+
+void 
+cm_GiveUpAllCallbacks(cm_server_t *tsp)
+{
+    long code;
+    cm_conn_t *connp;
+    struct rx_connection * rxconnp;
+
+    if (tsp->type == CM_SERVER_FILE) {
+        code = cm_ConnByServer(tsp, cm_rootUserp, &connp);
+        if (code == 0) {
+            rxconnp = cm_GetRxConn(connp);
+           code = RXAFS_GiveUpAllCallBacks(rxconnp);
+           rx_PutConnection(rxconnp);
+
+            lock_ObtainMutex(&tsp->mx);
+#ifdef GIVE_UP_CALLBACKS
+            cm_FreeGiveUpCallBackList(tsp);
+#endif /* GIVE_UP_CALLBACKS */
+            lock_ReleaseMutex(&tsp->mx);
+        }
+    }
+}
+
+void
+cm_GiveUpAllCallbacksAllServers(void)
+{
+    cm_server_t *tsp;
+
+    lock_ObtainWrite(&cm_serverLock);
+    for (tsp = cm_allServersp; tsp; tsp = tsp->allNextp) {
+        cm_GetServerNoLock(tsp);
+        lock_ReleaseWrite(&cm_serverLock);
+        cm_GiveUpAllCallbacks(tsp);
+        lock_ObtainWrite(&cm_serverLock);
+        cm_PutServerNoLock(tsp);
+    }
+    lock_ReleaseWrite(&cm_serverLock);
+}
+
+
index 013777e..bb9c735 100644 (file)
@@ -68,4 +68,16 @@ extern osi_rwlock_t cm_callbackLock;
 
 extern void cm_CallbackNotifyChange(cm_scache_t *scp);
 
+#ifdef GIVE_UP_CALLBACKS
+extern void cm_GiveUpCallback(struct cm_scache *);
+
+extern void cm_GiveUpCallBacksToServer(cm_server_t * serverp);
+
+#define AFS_MAXCBRSCALL   16      /* max to return in a given call */
+#endif /* GIVE_UP_CALLBACKS */
+
+extern void cm_GiveUpAllCallbacks(cm_server_t *tsp);
+
+extern void cm_GiveUpAllCallbacksAllServers(void);
+
 #endif /*  _CM_CALLBACK_H_ENV__ */
index 6863ba2..70b1e22 100644 (file)
@@ -160,17 +160,23 @@ long cm_RecycleSCache(cm_scache_t *scp, afs_int32 flags)
     scp->bulkStatProgress = hzero;
     scp->waitCount = 0;
 
-    scp->fid.vnode = 0;
-    scp->fid.volume = 0;
-    scp->fid.unique = 0;
-    scp->fid.cell = 0;
-
+#ifdef GIVE_UP_CALLBACKS
     /* discard callback */
     if (scp->cbServerp) {
-       cm_PutServer(scp->cbServerp);
-       scp->cbServerp = NULL;
+        cm_GiveUpCallback(scp);
+    }
+#else /* GIVE_UP_CALLBACKS */
+    if (scp->cbServerp) {
+        cm_PutServer(scp->cbServerp);
+        scp->cbServerp = NULL;
     }
     scp->cbExpires = 0;
+#endif /* GIVE_UP_CALLBACKS */
+
+    scp->fid.vnode = 0;
+    scp->fid.volume = 0;
+    scp->fid.unique = 0;
+    scp->fid.cell = 0;
 
     /* remove from dnlc */
     cm_dnlcPurgedp(scp);
@@ -447,11 +453,33 @@ cm_ValidateSCache(void)
     return cm_dnlcValidate();
 }
 
+void
+cm_SuspendSCache(void)
+{
+    cm_scache_t * scp;
+
+    cm_GiveUpAllCallbacksAllServers();
+
+    lock_ObtainWrite(&cm_scacheLock);
+    for ( scp = cm_data.allSCachesp; scp;
+          scp = scp->allNextp ) {
+        if (scp->cbServerp) {
+            cm_PutServer(scp->cbServerp);
+            scp->cbServerp = NULL;
+        }
+        scp->cbExpires = 0;
+        scp->flags &= ~CM_SCACHEFLAG_CALLBACK;
+    }
+    lock_ReleaseWrite(&cm_scacheLock);
+}
+
 long
 cm_ShutdownSCache(void)
 {
     cm_scache_t * scp;
 
+    lock_ObtainWrite(&cm_scacheLock);
+
     for ( scp = cm_data.allSCachesp; scp;
           scp = scp->allNextp ) {
         if (scp->randomACLp) {
@@ -459,9 +487,20 @@ cm_ShutdownSCache(void)
             cm_FreeAllACLEnts(scp);
             lock_ReleaseMutex(&scp->mx);
         }
+
+        if (scp->cbServerp) {
+            cm_PutServer(scp->cbServerp);
+            scp->cbServerp = NULL;
+        }
+        scp->cbExpires = 0;
+        scp->flags &= ~CM_SCACHEFLAG_CALLBACK;
+
         lock_FinalizeMutex(&scp->mx);
         lock_FinalizeRWLock(&scp->bufCreateLock);
     }
+    lock_ReleaseWrite(&cm_scacheLock);
+
+    cm_GiveUpAllCallbacksAllServers();
 
     return cm_dnlcShutdown();
 }
index d045143..0d28fbf 100644 (file)
@@ -375,6 +375,8 @@ extern long cm_ValidateSCache(void);
 
 extern long cm_ShutdownSCache(void);
 
+extern void cm_SuspendSCache(void);
+
 extern long cm_RecycleSCache(cm_scache_t *scp, afs_int32 flags);
 
 extern void cm_RemoveSCacheFromHashTable(cm_scache_t *scp);
index 827f65b..ac93fd4 100644 (file)
@@ -232,6 +232,17 @@ void cm_CheckServers(long flags, cm_cell_t *cellp)
         if (doPing) 
            cm_PingServer(tsp);
 
+#ifdef GIVE_UP_CALLBACKS
+        /* if this is a file server and it is not currently down
+         * give up any callbacks we have queued 
+         */
+        if (isFS && !(tsp->flags & CM_SERVERFLAG_DOWN)) {
+            lock_ObtainMutex(&tsp->mx);
+            cm_GiveUpCallBacksToServer(tsp);
+            lock_ReleaseMutex(&tsp->mx);
+        }
+#endif /* GIVE_UP_CALLBACKS */
+
         /* also, run the GC function for connections on all of the
          * server's connections.
          */
@@ -383,6 +394,22 @@ cm_server_t *cm_NewServer(struct sockaddr_in *socketp, int type, cm_cell_t *cell
     return tsp;
 }
 
+cm_server_t *
+cm_FindServerByIP(afs_uint32 ipaddr, int type)
+{
+    cm_server_t *tsp;
+
+    lock_ObtainRead(&cm_serverLock);
+    for (tsp = cm_allServersp; tsp; tsp = tsp->allNextp) {
+        if (tsp->type == type &&
+            tsp->addr.sin_addr.S_un.S_addr == ipaddr)
+            break;
+    }
+    lock_ReleaseRead(&cm_serverLock);
+
+    return tsp;
+}
+
 /* find a server based on its properties */
 cm_server_t *cm_FindServer(struct sockaddr_in *addrp, int type)
 {
@@ -711,18 +738,77 @@ void cm_FreeServerList(cm_serverRef_t** list, afs_uint32 flags)
     lock_ReleaseWrite(&cm_serverLock);
 }
 
-cm_server_t *
-cm_FindServerByIP(afs_uint32 ipaddr)
+#ifdef GIVE_UP_CALLBACKS
+cm_server_gucb_t *cm_NewServerGUCBs(void) {
+    cm_server_gucb_t *gucbp;
+
+    gucbp = malloc(sizeof(*gucbp));
+    if (gucbp)
+        memset(gucbp, 0, sizeof(*gucbp));
+
+    return gucbp;
+}
+
+
+/* server mutex must be held */
+void cm_AddFidToGiveUpCallBackList(cm_server_t * serverp, cm_fid_t *fidp) {
+    cm_server_gucb_t ** gucbpp;
+
+    for ( gucbpp = &serverp->gucbs; *gucbpp; gucbpp = &(*gucbpp)->nextp ) {
+        if ((*gucbpp)->count < AFS_MAXCBRSCALL) {
+            (*gucbpp)->fids[(*gucbpp)->count] = *fidp;
+            (*gucbpp)->count++;
+            return;
+        }
+    }
+
+    /* if we get here all of the allocated pages are full */
+    (*gucbpp) = cm_NewServerGUCBs();
+    if (*gucbpp) {
+        (*gucbpp)->fids[0] = *fidp;
+        (*gucbpp)->count = 1;
+    }
+}
+
+/* server mutex must be held */
+void cm_RemoveFidFromGiveUpCallBackList(cm_server_t *serverp, cm_fid_t *fidp) {
+    cm_server_gucb_t *gucbp;
+    int i;
+
+    for ( gucbp = serverp->gucbs; gucbp; gucbp = gucbp->nextp ) {
+        for ( i=0; i < gucbp->count; i++ ) {
+            if (cm_FidCmp(&gucbp->fids[i], fidp) == 0) {
+                /* invalidate this entry.  we will skip over it later */
+                gucbp->fids[i].cell = 0;
+                break;
+            }
+        }
+    }
+}
+
+/* server mutex must be held */
+void cm_FreeGiveUpCallBackList(cm_server_t * serverp)
+{
+    cm_server_gucb_t *gucbp, *nextp;
+
+    for ( gucbp = serverp->gucbs, serverp->gucbs = NULL; gucbp; gucbp = nextp ) {
+        nextp = gucbp->nextp;
+        free(gucbp);
+    }
+}
+
+void cm_FreeAllGiveUpCallBackLists(void)
 {
     cm_server_t *tsp;
 
     lock_ObtainRead(&cm_serverLock);
     for (tsp = cm_allServersp; tsp; tsp = tsp->allNextp) {
-        if (tsp->addr.sin_addr.S_un.S_addr == ipaddr)
-            break;
+        if (tsp->type == CM_SERVER_FILE && tsp->gucbs != NULL) {
+            lock_ObtainMutex(&tsp->mx);
+            cm_FreeGiveUpCallBackList(tsp);
+            lock_ReleaseMutex(&tsp->mx);
+        }
     }
     lock_ReleaseRead(&cm_serverLock);
-
-    return tsp;
 }
-
+#endif /* GIVE_UP_CALLBACKS */
index ba07030..5e8ad39 100644 (file)
@@ -20,6 +20,15 @@ typedef struct cm_server_vols {
     struct cm_server_vols *nextp;
 } cm_server_vols_t;
 
+#ifdef GIVE_UP_CALLBACKS
+#define AFS_MAXCBRSCALL 16
+typedef struct cm_server_gucb {
+    afs_uint32          count;
+    cm_fid_t            fids[AFS_MAXCBRSCALL];
+    struct cm_server_gucb * nextp;
+} cm_server_gucb_t;
+#endif /* GIVE_UP_CALLBACKS */
+
 /* pointed to by volumes and cells without holds; cm_serverLock is obtained
  * at the appropriate times to change the pointers to these servers.
  */
@@ -36,6 +45,9 @@ typedef struct cm_server {
     osi_mutex_t mx;
     unsigned short ipRank;             /* server priority */
     cm_server_vols_t *  vols;           /* by mx */
+#ifdef GIVE_UP_CALLBACKS
+    cm_server_gucb_t *  gucbs;          /* by mx */
+#endif /* GIVE_UP_CALLBACKS */
 } cm_server_t;
 
 enum repstate {srv_not_busy, srv_busy, srv_offline, srv_deleted};
@@ -121,6 +133,17 @@ extern void cm_SetServerNo64Bit(cm_server_t * serverp, int no64bit);
 
 extern void cm_SetServerNoInlineBulk(cm_server_t * serverp, int no);
 
-extern cm_server_t * cm_FindServerByIP(afs_uint32 addr);
+extern cm_server_t * cm_FindServerByIP(afs_uint32 addr, int type);
+
+#ifdef GIVE_UP_CALLBACKS
+extern cm_server_gucb_t *cm_NewServerGUCBs(void);
+
+extern void cm_AddFidToGiveUpCallBackList(cm_server_t * serverp, cm_fid_t *fidp);
+
+extern void cm_RemoveFidFromGiveUpCallBackList(cm_server_t *serverp, cm_fid_t *fidp);
+
+extern void cm_FreeGiveUpCallBackList(cm_server_t * serverp);
 
+extern void cm_FreeAllGiveUpCallBackLists(void);
+#endif /* GIVE_UP_CALLBACKS */
 #endif /*  __CM_SERVER_H_ENV__ */