예제 #1
0
/*!
 * Bump given cell up to the front of the LRU queue.
 * \param c Cell to set.
 */
static void
afs_UpdateCellLRU(struct cell *c)
{
    ObtainWriteLock(&afs_xcell, 100);
    QRemove(&c->lruq);
    QAdd(&CellLRU, &c->lruq);
    ReleaseWriteLock(&afs_xcell);
}
예제 #2
0
/*******************************************************************************
* Procedure:    RFU_GetRxChar
* Purpose:      xxxx
* Passed:       xxxx
*   
* Returned:     nothing
* Globals:      none
*
* Date:         Author:             Comments:
*   2014-09-19  Neal Shurmantine    initial revision
*******************************************************************************/
bool RFU_GetRxChar(unsigned char *rslt)
{
    if (QEmpty(&RxQueue) == false ) {
        QRemove(rslt,&RxQueue);
        if (QEmpty(&RxQueue) == true) {
            OS_EventClear(ActiveEventHandle,ActiveEventBit);
        }
        return true;
    }
    return false;
}
예제 #3
0
/* afs_DequeueCallback
 * Takes a write-locked vcache pointer and removes it from the callback
 * hash table, without knowing beforehand which slot it was in.
 *
 * for now, just get a lock on everything when doing the dequeue, don't
 * worry about getting a lock on the individual slot.
 * 
 * the only other places that do anything like dequeues are CheckCallbacks
 * and BumpBase.
 *
 * NOTE: The caller must hold a write lock on afs_xcbhash
 */
void
afs_DequeueCallback(struct vcache *avc)
{

    debugvc = avc;
    if (avc->callsort.prev) {
	QRemove(&(avc->callsort));
    } else;			/* must have got dequeued in a race */

    return;
}				/* afs_DequeueCallback */
예제 #4
0
static void DoReplace( void )
/***************************/
// this supports concatenated object decks and libraries (PageLen != 0)
{
    FlushBuffer();
    if( PageLen != 0 ) {        // NYI - spawning WLIB every time is
        close( OutFile );       // rather slow. Replace this somehow??
        OutFile = NOFILE;
        if( spawnlp( P_WAIT, WLIB_EXE, WLIB_EXE, TEMP_LIB_NAME, "-b", "+" TEMP_OBJ_NAME, NULL ) != 0 ) {
            Error( "problem with temporary library" );
        }
        QRemove( TEMP_OBJ_NAME );
    }
}
예제 #5
0
파일: queue.c 프로젝트: mingpen/OpenNT
void 
QFree(PQUEUE Queue)
/*++

Routine Description:

    This function removes any items left on the queue, frees them, and
    then frees the queue itself.

Arguments:

    Queue -- Pointer to the queue to free.

Return Value:

    None.

--*/
{
  
    LPVOID Object; // points to objects we pull off the queue

    EnterCriticalSection(&Queue->CrSec);

    // Free everything on the queue.
    Object = QRemove(Queue);
    while (Object) {
        free(Object);
        Object = QRemove(Queue);
    }

    LeaveCriticalSection(&Queue->CrSec);

    // Free the queue itself.
    free(Queue);
}
예제 #6
0
/*!
 * This function throws away the whole disconnected state, allowing
 * the cache manager to reconnect to a server if we get into a state
 * where reconiliation is impossible.
 *
 * \param acred
 *
 */
void
afs_DisconDiscardAll(afs_ucred_t *acred)
{
    struct vcache *tvc;

    ObtainWriteLock(&afs_disconDirtyLock, 717);
    while (!QEmpty(&afs_disconDirty)) {
	tvc = QEntry(QPrev(&afs_disconDirty), struct vcache, dirtyq);
	QRemove(&tvc->dirtyq);
	ReleaseWriteLock(&afs_disconDirtyLock);

	ObtainWriteLock(&tvc->lock, 718);
	afs_ResetVCache(tvc, acred, 0);
	tvc->f.truncPos = AFS_NOTRUNC;
	ReleaseWriteLock(&tvc->lock);
	ObtainWriteLock(&afs_disconDirtyLock, 719);
	afs_PutVCache(tvc);
    }

    afs_DisconDiscardAllShadows(1, acred);

    ReleaseWriteLock(&afs_disconDirtyLock);
}
예제 #7
0
/* afs_FlushCBs 
 * to be used only in dire circumstances, this drops all callbacks on
 * the floor, without giving them back to the server.  It's ok, the server can 
 * deal with it, but it is a little bit rude.
 */
void
afs_FlushCBs(void)
{
    register int i;
    register struct vcache *tvc;

    ObtainWriteLock(&afs_xcbhash, 86);	/* pretty likely I'm going to remove something */

    for (i = 0; i < VCSIZE; i++)	/* reset all the vnodes */
	for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
	    tvc->callback = 0;
	    tvc->dchint = NULL;	/* invalidate hints */
	    tvc->f.states &= ~(CStatd);
	    if (QPrev(&(tvc->callsort)))
		QRemove(&(tvc->callsort));
	    if (!(tvc->f.states & (CVInit|CVFlushed)) &&
                ((tvc->f.fid.Fid.Vnode & 1) || (vType(tvc) == VDIR)))
		osi_dnlc_purgedp(tvc);
	}

    afs_InitCBQueue(0);

    ReleaseWriteLock(&afs_xcbhash);
}
예제 #8
0
static void ProcFile( char * fname )
/**********************************/
{
    int         ftype;
    char *      name;
    int         status;
    int         namelen;
    char *      bak;

    namelen = strlen( fname ) + 5;
    name = alloca( namelen );
    if( name == NULL ) Suicide();           // null == no stack space left.
    strcpy( name, fname );
    ReplaceExt( name, ".obj", FALSE );
    InFile = QOpen( name, O_RDONLY | O_BINARY, 0 );
    for(;;) {
        CleanRecStuff();
        ftype = ReadRec();
        if( ftype == ENDLIBRARY || ftype == ENDFILE ) {
            break;
        } else if( ftype == LIBRARY ) {
            Warning( "exclude option does not apply to libraries" );
            FreeList( ExcludeList );
            ExcludeList = NULL;
        } else if( ftype != OBJECT ) {
            Error( "file is not a standard OBJECT or LIBRARY file" );
        }
        OutFile = QOpen( TEMP_OBJ_NAME, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0 );
        do {
            ProcessRec();
            status = ReadRec();
        } while( status == OK );
        if( status == ENDMODULE ) {
            ProcessRec();           // process the modend rec.
            DoReplace();
        } else {
            Error( "premature end of file encountered" );
        }
        FreeList( ExcludeList );    // do this here so concatenated .obj files
        ExcludeList = NULL;         // only have the first module excluded.
    }
    CloseFiles();
    if( MakeBackup ) {
        bak = alloca( namelen );
        if( bak == NULL ) Suicide();           // null == no stack space left.
        strcpy( bak, name );
        if( ftype == ENDLIBRARY ) {
            ReplaceExt( bak, ".bak", TRUE );
        } else {
            ReplaceExt( bak, ".bob", TRUE );
        }
        CopyFile( name, bak );
    }
    QRemove( name );
    if( ftype == ENDLIBRARY ) {
        rename( TEMP_LIB_NAME, name );
    } else {
        rename( TEMP_OBJ_NAME, name );
    }
    FileCleanup();
}
예제 #9
0
/*!
 * Handles all the reconnection details:
 * - Get all the details about the vnode: name, fid, and parent dir fid.
 * - Send data to server.
 * - Handle errors.
 * - Reorder vhash and dcaches in their hashes, using the newly acquired fid.
 */
int
afs_ProcessOpCreate(struct vcache *avc, struct vrequest *areq,
		    afs_ucred_t *acred)
{
    char *tname = NULL, *ttargetName = NULL;
    struct AFSStoreStatus InStatus;
    struct AFSFetchStatus OutFidStatus, OutDirStatus;
    struct VenusFid pdir_fid, newFid;
    struct AFSCallBack CallBack;
    struct AFSVolSync tsync;
    struct vcache *tdp = NULL, *tvc = NULL;
    struct dcache *tdc = NULL;
    struct afs_conn *tc;
    struct rx_connection *rxconn;
    afs_int32 hash, new_hash, index;
    afs_size_t tlen;
    int code, op = 0;
    XSTATS_DECLS;

    tname = afs_osi_Alloc(AFSNAMEMAX);
    if (!tname)
	return ENOMEM;

    code = afs_GetParentVCache(avc, 0, &pdir_fid, tname, &tdp);
    if (code)
	goto end;

    /* This data may also be in linkData, but then we have to deal with
     * the joy of terminating NULLs and . and file modes. So just get
     * it from the dcache where it won't have been fiddled with.
     */
    if (vType(avc) == VLNK) {
	afs_size_t offset;
	struct dcache *tdc;
	struct osi_file *tfile;

	tdc = afs_GetDCache(avc, 0, areq, &offset, &tlen, 0);
	if (!tdc) {
	    code = ENOENT;
	    goto end;
	}

	if (tlen > 1024) {
	    afs_PutDCache(tdc);
	    code = EFAULT;
	    goto end;
	}

	tlen++; /* space for NULL */
	ttargetName = afs_osi_Alloc(tlen);
	if (!ttargetName) {
	    afs_PutDCache(tdc);
	    return ENOMEM;
	}
	ObtainReadLock(&tdc->lock);
	tfile = afs_CFileOpen(&tdc->f.inode);
	code = afs_CFileRead(tfile, 0, ttargetName, tlen);
	ttargetName[tlen-1] = '\0';
	afs_CFileClose(tfile);
	ReleaseReadLock(&tdc->lock);
	afs_PutDCache(tdc);
    }

    /* Set status. */
    InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP;
    InStatus.ClientModTime = avc->f.m.Date;
    InStatus.Owner = avc->f.m.Owner;
    InStatus.Group = (afs_int32) afs_cr_gid(acred);
    /* Only care about protection bits. */
    InStatus.UnixModeBits = avc->f.m.Mode & 0xffff;

    do {
	tc = afs_Conn(&tdp->f.fid, areq, SHARED_LOCK, &rxconn);
	if (tc) {
	    switch (vType(avc)) {
	    case VREG:
                /* Make file on server. */
		op = AFS_STATS_FS_RPCIDX_CREATEFILE;
		XSTATS_START_TIME(op);
                RX_AFS_GUNLOCK();
                code = RXAFS_CreateFile(tc->id,
					(struct AFSFid *)&tdp->f.fid.Fid,
					tname, &InStatus,
					(struct AFSFid *) &newFid.Fid,
					&OutFidStatus, &OutDirStatus,
					&CallBack, &tsync);
                RX_AFS_GLOCK();
                XSTATS_END_TIME;
		break;
	    case VDIR:
		/* Make dir on server. */
		op = AFS_STATS_FS_RPCIDX_MAKEDIR;
		XSTATS_START_TIME(op);
                RX_AFS_GUNLOCK();
		code = RXAFS_MakeDir(rxconn, (struct AFSFid *) &tdp->f.fid.Fid,
				     tname, &InStatus,
				     (struct AFSFid *) &newFid.Fid,
				     &OutFidStatus, &OutDirStatus,
				     &CallBack, &tsync);
                RX_AFS_GLOCK();
                XSTATS_END_TIME;
		break;
	    case VLNK:
		/* Make symlink on server. */
		op = AFS_STATS_FS_RPCIDX_SYMLINK;
		XSTATS_START_TIME(op);
		RX_AFS_GUNLOCK();
		code = RXAFS_Symlink(rxconn,
				(struct AFSFid *) &tdp->f.fid.Fid,
				tname, ttargetName, &InStatus,
				(struct AFSFid *) &newFid.Fid,
				&OutFidStatus, &OutDirStatus, &tsync);
		RX_AFS_GLOCK();
		XSTATS_END_TIME;
	        break;
	    default:
		op = AFS_STATS_FS_RPCIDX_CREATEFILE;
		code = 1;
		break;
	    }
        } else
	    code = -1;
    } while (afs_Analyze(tc, rxconn, code, &tdp->f.fid, areq, op, SHARED_LOCK, NULL));

    /* TODO: Handle errors. */
    if (code) {
	/* printf("afs_ProcessOpCreate: error while creating vnode on server, code=%d .\n", code); */
	goto end;
    }

    /* The rpc doesn't set the cell number. */
    newFid.Cell = avc->f.fid.Cell;

    /*
     * Change the fid in the dir entry.
     */

    /* Seek the dir's dcache. */
    tdc = afs_FindDCacheByFid(&tdp->f.fid);
    if (tdc) {
    	/* And now change the fid in the parent dir entry. */
    	afs_dir_ChangeFid(tdc, tname, &avc->f.fid.Fid.Vnode, &newFid.Fid.Vnode);
    	afs_PutDCache(tdc);
    }

    if (vType(avc) == VDIR) {
	/* Change fid in the dir for the "." entry. ".." has alredy been
	 * handled by afs_FixChildrenFids when processing the parent dir.
	 */
	tdc = afs_FindDCacheByFid(&avc->f.fid);
	if (tdc) {
   	    afs_dir_ChangeFid(tdc, ".", &avc->f.fid.Fid.Vnode,
			      &newFid.Fid.Vnode);

	    if (avc->f.m.LinkCount >= 2)
	        /* For non empty dirs, fix children's parentVnode and
		 * parentUnique reference.
	     	 */
	    	afs_FixChildrenFids(&avc->f.fid, &newFid);

	    afs_PutDCache(tdc);
	}
    }

    /* Recompute hash chain positions for vnode and dcaches.
     * Then change to the new FID.
     */

    /* The vcache goes first. */
    ObtainWriteLock(&afs_xvcache, 735);

    /* Old fid hash. */
    hash = VCHash(&avc->f.fid);
    /* New fid hash. */
    new_hash = VCHash(&newFid);

    /* Remove hash from old position. */
    /* XXX: not checking array element contents. It shouldn't be empty.
     * If it oopses, then something else might be wrong.
     */
    if (afs_vhashT[hash] == avc) {
        /* First in hash chain (might be the only one). */
	afs_vhashT[hash] = avc->hnext;
    } else {
        /* More elements in hash chain. */
 	for (tvc = afs_vhashT[hash]; tvc; tvc = tvc->hnext) {
	    if (tvc->hnext == avc) {
		tvc->hnext = avc->hnext;
		break;
	    }
        }
    }                           /* if (!afs_vhashT[i]->hnext) */
    QRemove(&avc->vhashq);

    /* Insert hash in new position. */
    avc->hnext = afs_vhashT[new_hash];
    afs_vhashT[new_hash] = avc;
    QAdd(&afs_vhashTV[VCHashV(&newFid)], &avc->vhashq);

    ReleaseWriteLock(&afs_xvcache);

    /* Do the same thing for all dcaches. */
    hash = DVHash(&avc->f.fid);
    ObtainWriteLock(&afs_xdcache, 743);
    for (index = afs_dvhashTbl[hash]; index != NULLIDX; index = hash) {
        hash = afs_dvnextTbl[index];
        tdc = afs_GetValidDSlot(index);
        ReleaseReadLock(&tdc->tlock);
	if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
            if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {

		/* Safer but slower. */
 		afs_HashOutDCache(tdc, 0);

                /* Put dcache in new positions in the dchash and dvhash. */
 		new_hash = DCHash(&newFid, tdc->f.chunk);
 		afs_dcnextTbl[tdc->index] = afs_dchashTbl[new_hash];
 		afs_dchashTbl[new_hash] = tdc->index;

 		new_hash = DVHash(&newFid);
 		afs_dvnextTbl[tdc->index] = afs_dvhashTbl[new_hash];
 		afs_dvhashTbl[new_hash] = tdc->index;

 		afs_indexUnique[tdc->index] = newFid.Fid.Unique;
		memcpy(&tdc->f.fid, &newFid, sizeof(struct VenusFid));
           }                   /* if fid match */
	}                       /* if uniquifier match */
    	if (tdc)
	    afs_PutDCache(tdc);
    }                           /* for all dcaches in this hash bucket */
    ReleaseWriteLock(&afs_xdcache);

    /* Now we can set the new fid. */
    memcpy(&avc->f.fid, &newFid, sizeof(struct VenusFid));

end:
    if (tdp)
    	afs_PutVCache(tdp);
    afs_osi_Free(tname, AFSNAMEMAX);
    if (ttargetName)
	afs_osi_Free(ttargetName, tlen);
    return code;
}
예제 #10
0
/*!
 * All files that have been dirty before disconnection are going to
 * be replayed back to the server.
 *
 * \param areq Request from the user.
 * \param acred User credentials.
 *
 * \return If all files synchronized succesfully, return 0, otherwise
 * return error code
 *
 * \note For now, it's the request from the PDiscon pioctl.
 *
 */
int
afs_ResyncDisconFiles(struct vrequest *areq, afs_ucred_t *acred)
{
    struct afs_conn *tc;
    struct rx_connection *rxconn;
    struct vcache *tvc;
    struct AFSFetchStatus fstat;
    struct AFSCallBack callback;
    struct AFSVolSync tsync;
    int code = 0;
    afs_int32 start = 0;
    XSTATS_DECLS;
    /*AFS_STATCNT(afs_ResyncDisconFiles);*/

    ObtainWriteLock(&afs_disconDirtyLock, 707);

    while (!QEmpty(&afs_disconDirty)) {
	tvc = QEntry(QPrev(&afs_disconDirty), struct vcache, dirtyq);

	/* Can't lock tvc whilst holding the discon dirty lock */
	ReleaseWriteLock(&afs_disconDirtyLock);

	/* Get local write lock. */
	ObtainWriteLock(&tvc->lock, 705);

	if (tvc->f.ddirty_flags & VDisconRemove) {
	    /* Delete the file on the server and just move on
	     * to the next file. After all, it has been deleted
	     * we can't replay any other operation it.
	     */
	    code = afs_ProcessOpRemove(tvc, areq);
	    goto next_file;

	} else if (tvc->f.ddirty_flags & VDisconCreate) {
	    /* For newly created files, we don't need a server lock. */
	    code = afs_ProcessOpCreate(tvc, areq, acred);
	    if (code)
	    	goto next_file;

	    tvc->f.ddirty_flags &= ~VDisconCreate;
	    tvc->f.ddirty_flags |= VDisconCreated;
	}
#if 0
  	/* Get server write lock. */
  	do {
	    tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK, &rxconn);
  	    if (tc) {
	    	XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_SETLOCK);
  		RX_AFS_GUNLOCK();
  		code = RXAFS_SetLock(rxconn,
					(struct AFSFid *)&tvc->f.fid.Fid,
					LockWrite,
					&tsync);
		RX_AFS_GLOCK();
		XSTATS_END_TIME;
	   } else
		code = -1;

	} while (afs_Analyze(tc,
			     rxconn,
			     code,
			     &tvc->f.fid,
			     areq,
			     AFS_STATS_FS_RPCIDX_SETLOCK,
			     SHARED_LOCK,
			     NULL));

	if (code)
	    goto next_file;
#endif
	if (tvc->f.ddirty_flags & VDisconRename) {
	    /* If we're renaming the file, do so now */
	    code = afs_ProcessOpRename(tvc, areq);
	    if (code)
	    	goto unlock_srv_file;
	}

	/* Issue a FetchStatus to get info about DV and callbacks. */
	do {
	    tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK, &rxconn);
	    if (tc) {
	    	tvc->callback = tc->srvr->server;
		start = osi_Time();
		XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHSTATUS);
		RX_AFS_GUNLOCK();
		code = RXAFS_FetchStatus(rxconn,
				(struct AFSFid *)&tvc->f.fid.Fid,
				&fstat,
				&callback,
				&tsync);
		RX_AFS_GLOCK();
		XSTATS_END_TIME;
	    } else
		code = -1;

	} while (afs_Analyze(tc,
			     rxconn,
			     code,
			     &tvc->f.fid,
			     areq,
			     AFS_STATS_FS_RPCIDX_FETCHSTATUS,
			     SHARED_LOCK,
			     NULL));

	if (code) {
	    goto unlock_srv_file;
	}

	if ((dv_match(tvc, fstat) && (tvc->f.m.Date == fstat.ServerModTime)) ||
	    	(afs_ConflictPolicy == CLIENT_WINS) ||
		(tvc->f.ddirty_flags & VDisconCreated)) {
	    /*
	     * Send changes to the server if there's data version match, or
	     * client wins policy has been selected or file has been created
	     * but doesn't have it's the contents on to the server yet.
	     */
	   /*
	    * XXX: Checking server attr changes by timestamp might not the
	    * most elegant solution, but it's the most viable one that we could find.
	    */
	    afs_UpdateStatus(tvc, &tvc->f.fid, areq, &fstat, &callback, start);
	    code = afs_SendChanges(tvc, areq);

	} else if (afs_ConflictPolicy == SERVER_WINS) {
	    /* DV mismatch, apply collision resolution policy. */
	    /* Discard this files chunks and remove from current dir. */
	    afs_ResetVCache(tvc, acred, 0);
	    tvc->f.truncPos = AFS_NOTRUNC;
	} else {
	    /* printf("afs_ResyncDisconFiles: no resolution policy selected.\n"); */
	}		/* if DV match or client wins policy */

unlock_srv_file:
	/* Release server write lock. */
#if 0
	do {
	    tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK, &rxconn);
	    if (tc) {
	    	XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RELEASELOCK);
	    	RX_AFS_GUNLOCK();
		ucode = RXAFS_ReleaseLock(rxconn,
				(struct AFSFid *) &tvc->f.fid.Fid,
				&tsync);
		RX_AFS_GLOCK();
		XSTATS_END_TIME;
	    } else
		ucode = -1;
	} while (afs_Analyze(tc,
			     rxconn,
			     ucode,
			     &tvc->f.fid,
			     areq,
			     AFS_STATS_FS_RPCIDX_RELEASELOCK,
			     SHARED_LOCK,
			     NULL));
#endif
next_file:
	ObtainWriteLock(&afs_disconDirtyLock, 710);
	if (code == 0) {
	    /* Replayed successfully - pull the vcache from the
	     * disconnected list */
	    tvc->f.ddirty_flags = 0;
	    QRemove(&tvc->dirtyq);
	    afs_PutVCache(tvc);
	} else {
	    if (code == EAGAIN)	{
		/* Operation was deferred. Pull it from the current place in
		 * the list, and stick it at the end again */
		QRemove(&tvc->dirtyq);
	   	QAdd(&afs_disconDirty, &tvc->dirtyq);
	    } else {
		/* Failed - keep state as is, and let the user know we died */

    		ReleaseWriteLock(&tvc->lock);
		break;
	    }
	}

	/* Release local write lock. */
	ReleaseWriteLock(&tvc->lock);
    }			/* while (tvc) */

    if (code) {
        ReleaseWriteLock(&afs_disconDirtyLock);
	return code;
    }

    /* Dispose of all of the shadow directories */
    afs_DisconDiscardAllShadows(0, acred);

    ReleaseWriteLock(&afs_disconDirtyLock);
    return code;
}
예제 #11
0
void
afs_CheckCallbacks(unsigned int secs)
{
    struct vcache *tvc;
    register struct afs_q *tq;
    struct afs_q *uq;
    afs_uint32 now;
    struct volume *tvp;
    register int safety;

    ObtainWriteLock(&afs_xcbhash, 85);	/* pretty likely I'm going to remove something */
    now = osi_Time();
    for (safety = 0, tq = cbHashT[base].head.prev;
	 (safety <= CBQ_LIMIT) && (tq != &(cbHashT[base].head));
	 tq = uq, safety++) {

	uq = QPrev(tq);
	tvc = CBQTOV(tq);
	if (tvc->cbExpires < now + secs) {	/* race #1 here */
	    /* Get the volume, and if its callback expiration time is more than secs
	     * seconds into the future, update this vcache entry and requeue it below
	     */
	    if ((tvc->f.states & CRO)
		&& (tvp = afs_FindVolume(&(tvc->f.fid), READ_LOCK))) {
		if (tvp->expireTime > now + secs) {
		    tvc->cbExpires = tvp->expireTime;	/* XXX race here */
		} else {
		    int i;
		    for (i = 0; i < MAXHOSTS && tvp->serverHost[i]; i++) {
			if (!(tvp->serverHost[i]->flags & SRVR_ISDOWN)) {
			    /* What about locking xvcache or vrefcount++ or
			     * write locking tvc? */
			    QRemove(tq);
			    tvc->f.states &= ~(CStatd | CMValid | CUnique);
                            if (!(tvc->f.states & (CVInit|CVFlushed)) &&
                                (tvc->f.fid.Fid.Vnode & 1 ||
                                 (vType(tvc) == VDIR)))
				osi_dnlc_purgedp(tvc);
			    tvc->dchint = NULL;	/*invalidate em */
			    afs_ResetVolumeInfo(tvp);
			    break;
			}
		    }
		}
		afs_PutVolume(tvp, READ_LOCK);
	    } else {
		/* Do I need to worry about things like execsorwriters?
		 * What about locking xvcache or vrefcount++ or write locking tvc?
		 */
		QRemove(tq);
		tvc->f.states &= ~(CStatd | CMValid | CUnique);
                if (!(tvc->f.states & (CVInit|CVFlushed)) &&
                    (tvc->f.fid.Fid.Vnode & 1 || (vType(tvc) == VDIR)))
		    osi_dnlc_purgedp(tvc);
	    }
	}

	if ((tvc->cbExpires > basetime) && CBHash(tvc->cbExpires - basetime)) {
	    /* it's been renewed on us.  Have to be careful not to put it back
	     * into this slot, or we may never get out of here.
	     */
	    int slot;
	    slot = (CBHash(tvc->cbExpires - basetime) + base) % CBHTSIZE;
	    if (slot != base) {
		if (QPrev(tq))
		    QRemove(&(tvc->callsort));
		QAdd(&(cbHashT[slot].head), &(tvc->callsort));
		/* XXX remember to update volume expiration time */
		/* -- not needed for correctness, though */
	    }
	}
    }

    if (safety > CBQ_LIMIT) {
	afs_stats_cmperf.cbloops++;
	if (afs_paniconwarn)
	    osi_Panic("CheckCallbacks");

	afs_warn
	    ("AFS Internal Error (minor): please contact AFS Product Support.\n");
	ReleaseWriteLock(&afs_xcbhash);
	afs_FlushCBs();
	return;
    } else
	ReleaseWriteLock(&afs_xcbhash);


/* XXX future optimization:
   if this item has been recently accessed, queue up a stat for it.
   {
   struct dcache * adc;

   ObtainReadLock(&afs_xdcache);
   if ((adc = tvc->quick.dc) && (adc->stamp == tvc->quick.stamp)
   && (afs_indexTimes[adc->index] > afs_indexCounter - 20)) {
   queue up the stat request
   }
   ReleaseReadLock(&afs_xdcache);
   }
   */

    return;
}				/* afs_CheckCallback */