Ejemplo n.º 1
0
void Quaternion_Matrix_Fourier_Transform_ExpLeft_Inv(Quaternion ** QMatrix,Quaternion *** pQMatrixFT,
    Quaternion QMu,int intHeight,int intWidth)
{
	int s,t,S,T;
	Quaternion QExp,QSum;
	double dblFactor,dblQuotient;
	
	dblFactor = 2. * M_PI ;
	dblQuotient = sqrt((double)intHeight*(double)intWidth);
	
	// the frequency image coordinates will be describe
	// by the S and T index.
	// In the spatial image, the pixel will be pointed by the spatial 
	// coordinates s and t.

	for (s=0;s<intHeight;s++)
		for (t=0;t<intWidth;t++)
		{
			QSum = QInit(0.,0.,0.,0.);
			//QDisp(QSum,stdout);  
			for(S=0;S<intHeight;S++)
			{
				for(T=0;T<intWidth;T++)
				{
					QExp = QInitExp(QMu,dblFactor*(S*s/((double)intHeight)+T*t/((double)intWidth)));
					//QDisp(QExp,stdout);
					QSum = QAdd(QSum,QMult(QExp,QMatrix[S][T]));
				}
			}
			(*pQMatrixFT)[s][t].a = QSum.a/dblQuotient;		
			(*pQMatrixFT)[s][t].b = QSum.b/dblQuotient;	//	R(s,t)
			(*pQMatrixFT)[s][t].c = QSum.c/dblQuotient;	//	G(s,t)
			(*pQMatrixFT)[s][t].d = QSum.d/dblQuotient;	//	B(s,t)
		}
}
Ejemplo n.º 2
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);
}
Ejemplo n.º 3
0
void
afs_QueueCallback(struct vcache *avc, unsigned int atime, struct volume *avp)
{
    if (avp && (avp->expireTime < avc->cbExpires))
	avp->expireTime = avc->cbExpires;
    if (!(avc->callsort.next)) {
	atime = (atime + base) % CBHTSIZE;
	QAdd(&(cbHashT[atime].head), &(avc->callsort));
    }

    return;
}				/* afs_QueueCallback */
Ejemplo n.º 4
0
/*!
 * Create or update a cell entry.
 * \param acellName Name of cell.
 * \param acellHosts Array of hosts that this cell has.
 * \param aflags Cell flags.
 * \param linkedcname
 * \param fsport File server port.
 * \param vlport Volume server port.
 * \param timeout Cell timeout value, 0 means static AFSDB entry.
 * \return
 */
afs_int32
afs_NewCell(char *acellName, afs_int32 * acellHosts, int aflags,
	    char *linkedcname, u_short fsport, u_short vlport, int timeout)
{
    struct cell *tc, *tcl = 0;
    afs_int32 i, newc = 0, code = 0;

    AFS_STATCNT(afs_NewCell);

    ObtainWriteLock(&afs_xcell, 103);

    tc = afs_FindCellByName_nl(acellName, READ_LOCK);
    if (tc) {
	aflags &= ~CNoSUID;
    } else {
	tc = afs_osi_Alloc(sizeof(struct cell));
	osi_Assert(tc != NULL);
	memset(tc, 0, sizeof(*tc));
	tc->cellName = afs_strdup(acellName);
	tc->fsport = AFS_FSPORT;
	tc->vlport = AFS_VLPORT;
	AFS_MD5_String(tc->cellHandle, tc->cellName, strlen(tc->cellName));
	AFS_RWLOCK_INIT(&tc->lock, "cell lock");
	newc = 1;
	aflags |= CNoSUID;
    }
    ObtainWriteLock(&tc->lock, 688);

    /* If the cell we've found has the correct name but no timeout,
     * and we're called with a non-zero timeout, bail out:  never
     * override static configuration entries with AFSDB ones.
     * One exception: if the original cell entry had no servers,
     * it must get servers from AFSDB.
     */
    if (timeout && !tc->timeout && tc->cellHosts[0]) {
	code = EEXIST;		/* This code is checked for in afs_LookupAFSDB */
	goto bad;
    }

    /* we don't want to keep pinging old vlservers which were down,
     * since they don't matter any more.  It's easier to do this than
     * to remove the server from its various hash tables. */
    for (i = 0; i < AFS_MAXCELLHOSTS; i++) {
	if (!tc->cellHosts[i])
	    break;
	tc->cellHosts[i]->flags &= ~SRVR_ISDOWN;
	tc->cellHosts[i]->flags |= SRVR_ISGONE;
    }

    if (fsport)
	tc->fsport = fsport;
    if (vlport)
	tc->vlport = vlport;

    if (aflags & CLinkedCell) {
	if (!linkedcname) {
	    code = EINVAL;
	    goto bad;
	}
	tcl = afs_FindCellByName_nl(linkedcname, READ_LOCK);
	if (!tcl) {
	    code = ENOENT;
	    goto bad;
	}
	if (tcl->lcellp) {	/* XXX Overwriting if one existed before! XXX */
	    tcl->lcellp->lcellp = (struct cell *)0;
	    tcl->lcellp->states &= ~CLinkedCell;
	}
	tc->lcellp = tcl;
	tcl->lcellp = tc;
    }
    tc->states |= aflags;
    tc->timeout = timeout;

    memset(tc->cellHosts, 0, sizeof(tc->cellHosts));
    for (i = 0; i < AFS_MAXCELLHOSTS; i++) {
	/* Get server for each host and link this cell in.*/
	struct server *ts;
	afs_uint32 temp = acellHosts[i];
	if (!temp)
	    break;
	ts = afs_GetServer(&temp, 1, 0, tc->vlport, WRITE_LOCK, NULL, 0);
	ts->cell = tc;
	ts->flags &= ~SRVR_ISGONE;
	/* Set the server as a host of the new cell. */
	tc->cellHosts[i] = ts;
	afs_PutServer(ts, WRITE_LOCK);
    }
    afs_SortServers(tc->cellHosts, AFS_MAXCELLHOSTS);	/* randomize servers */

    /* New cell: Build and add to LRU cell queue. */
    if (newc) {
	struct cell_name *cn;

	cn = afs_cellname_lookup_name(acellName);
	if (!cn)
	    cn = afs_cellname_new(acellName, 0);

	tc->cnamep = cn;
	tc->cellNum = cn->cellnum;
	tc->cellIndex = afs_cellindex++;
	afs_stats_cmperf.numCellsVisible++;
	QAdd(&CellLRU, &tc->lruq);
    }

    ReleaseWriteLock(&tc->lock);
    ReleaseWriteLock(&afs_xcell);
    afs_PutCell(tc, 0);
    if (!(aflags & CHush))
	afs_DynrootInvalidate();
    return 0;

  bad:
    if (newc) {
	afs_osi_FreeStr(tc->cellName);
	afs_osi_Free(tc, sizeof(struct cell));
    }
    ReleaseWriteLock(&tc->lock);
    ReleaseWriteLock(&afs_xcell);
    return code;
}
Ejemplo n.º 5
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;
}
Ejemplo n.º 6
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;
}
Ejemplo n.º 7
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 */