/* Try to discard pages, in order to recycle a vcache entry. * * We also make some sanity checks: ref count, open count, held locks. * * We also do some non-VM-related chores, such as releasing the cred pointer * (for AIX and Solaris) and releasing the gnode (for AIX). * * Locking: afs_xvcache lock is held. If it is dropped and re-acquired, * *slept should be set to warn the caller. * * Formerly, afs_xvcache was dropped and re-acquired for Solaris, but now it * is not dropped and re-acquired for any platform. It may be that *slept is * therefore obsolescent. * * OSF/1 Locking: VN_LOCK has been called. * We do not lock the vnode here, but instead require that it be exclusive * locked by code calling osi_VM_StoreAllSegments directly, or scheduling it * from the bqueue - Matt * Maybe better to just call vnode_pager_setsize()? */ int osi_VM_FlushVCache(struct vcache *avc, int *slept) { struct vm_object *obj; struct vnode *vp; if (VREFCOUNT(avc) > 1) return EBUSY; if (avc->opens) return EBUSY; /* if a lock is held, give up */ if (CheckLock(&avc->lock)) return EBUSY; return(0); AFS_GUNLOCK(); vp = AFSTOV(avc); #ifndef AFS_FBSD70_ENV lock_vnode(vp); #endif if (VOP_GETVOBJECT(vp, &obj) == 0) { VM_OBJECT_LOCK(obj); vm_object_page_remove(obj, 0, 0, FALSE); #if 1 if (obj->ref_count == 0) { simple_lock(&vp->v_interlock); vgonel(vp, curthread); vp->v_tag = VT_AFS; SetAfsVnode(vp); } #endif VM_OBJECT_UNLOCK(obj); } #ifndef AFS_FBSD70_ENV unlock_vnode(vp); #endif AFS_GLOCK(); return 0; }
int osi_TryEvictVCache(struct vcache *avc, int *slept, int defersleep) { int code; struct dentry *dentry; struct list_head *cur, *head; /* First, see if we can evict the inode from the dcache */ if (defersleep && avc != afs_globalVp && VREFCOUNT(avc) > 1 && avc->opens == 0) { *slept = 1; ReleaseWriteLock(&afs_xvcache); AFS_GUNLOCK(); afs_linux_lock_dcache(); head = &(AFSTOV(avc))->i_dentry; restart: cur = head; while ((cur = cur->next) != head) { dentry = list_entry(cur, struct dentry, d_alias); if (d_unhashed(dentry)) continue; dget_locked(dentry); afs_linux_unlock_dcache(); if (d_invalidate(dentry) == -EBUSY) { dput(dentry); /* perhaps lock and try to continue? (use cur as head?) */ goto inuse; } dput(dentry); afs_linux_lock_dcache(); goto restart; } afs_linux_unlock_dcache(); inuse: AFS_GLOCK(); ObtainWriteLock(&afs_xvcache, 733); }
/* Note that we don't set CDirty here, this is OK because the unlink * RPC is called synchronously */ int afs_remove(OSI_VC_DECL(adp), char *aname, afs_ucred_t *acred) { struct vrequest treq; register struct dcache *tdc; struct VenusFid unlinkFid; register afs_int32 code; register struct vcache *tvc; afs_size_t offset, len; struct afs_fakestat_state fakestate; OSI_VC_CONVERT(adp); AFS_STATCNT(afs_remove); afs_Trace2(afs_iclSetp, CM_TRACE_REMOVE, ICL_TYPE_POINTER, adp, ICL_TYPE_STRING, aname); if ((code = afs_InitReq(&treq, acred))) { return code; } afs_InitFakeStat(&fakestate); AFS_DISCON_LOCK(); code = afs_EvalFakeStat(&adp, &fakestate, &treq); if (code) goto done; /* Check if this is dynroot */ if (afs_IsDynroot(adp)) { code = afs_DynrootVOPRemove(adp, acred, aname); goto done; } if (afs_IsDynrootMount(adp)) { code = ENOENT; goto done; } if (strlen(aname) > AFSNAMEMAX) { code = ENAMETOOLONG; goto done; } tagain: code = afs_VerifyVCache(adp, &treq); tvc = NULL; if (code) { code = afs_CheckCode(code, &treq, 23); goto done; } /** If the volume is read-only, return error without making an RPC to the * fileserver */ if (adp->f.states & CRO) { code = EROFS; goto done; } /* If we're running disconnected without logging, go no further... */ if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) { code = ENETDOWN; goto done; } tdc = afs_GetDCache(adp, (afs_size_t) 0, &treq, &offset, &len, 1); /* test for error below */ ObtainWriteLock(&adp->lock, 142); if (tdc) ObtainSharedLock(&tdc->lock, 638); /* * Make sure that the data in the cache is current. We may have * received a callback while we were waiting for the write lock. */ if (!(adp->f.states & CStatd) || (tdc && !hsame(adp->f.m.DataVersion, tdc->f.versionNo))) { ReleaseWriteLock(&adp->lock); if (tdc) { ReleaseSharedLock(&tdc->lock); afs_PutDCache(tdc); } goto tagain; } unlinkFid.Fid.Vnode = 0; if (!tvc) { tvc = osi_dnlc_lookup(adp, aname, WRITE_LOCK); } /* This should not be necessary since afs_lookup() has already * done the work. */ if (!tvc) if (tdc) { code = afs_dir_Lookup(tdc, aname, &unlinkFid.Fid); if (code == 0) { afs_int32 cached = 0; unlinkFid.Cell = adp->f.fid.Cell; unlinkFid.Fid.Volume = adp->f.fid.Fid.Volume; if (unlinkFid.Fid.Unique == 0) { tvc = afs_LookupVCache(&unlinkFid, &treq, &cached, adp, aname); } else { ObtainReadLock(&afs_xvcache); tvc = afs_FindVCache(&unlinkFid, 0, DO_STATS); ReleaseReadLock(&afs_xvcache); } } } if (AFS_IS_DISCON_RW) { if (!adp->f.shadow.vnode && !(adp->f.ddirty_flags & VDisconCreate)) { /* Make shadow copy of parent dir. */ afs_MakeShadowDir(adp, tdc); } /* Can't hold a dcache lock whilst we're getting a vcache one */ if (tdc) ReleaseSharedLock(&tdc->lock); /* XXX - We're holding adp->lock still, and we've got no * guarantee about whether the ordering matches the lock hierarchy */ ObtainWriteLock(&tvc->lock, 713); /* If we were locally created, then we don't need to do very * much beyond ensuring that we don't exist anymore */ if (tvc->f.ddirty_flags & VDisconCreate) { afs_DisconRemoveDirty(tvc); } else { /* Add removed file vcache to dirty list. */ afs_DisconAddDirty(tvc, VDisconRemove, 1); } adp->f.m.LinkCount--; ReleaseWriteLock(&tvc->lock); if (tdc) ObtainSharedLock(&tdc->lock, 714); } if (tvc && osi_Active(tvc)) { /* about to delete whole file, prefetch it first */ ReleaseWriteLock(&adp->lock); if (tdc) ReleaseSharedLock(&tdc->lock); ObtainWriteLock(&tvc->lock, 143); FetchWholeEnchilada(tvc, &treq); ReleaseWriteLock(&tvc->lock); ObtainWriteLock(&adp->lock, 144); /* Technically I don't think we need this back, but let's hold it anyway; The "got" reference should actually be sufficient. */ if (tdc) ObtainSharedLock(&tdc->lock, 640); } osi_dnlc_remove(adp, aname, tvc); Tadp1 = adp; #ifndef AFS_DARWIN80_ENV Tadpr = VREFCOUNT(adp); #endif Ttvc = tvc; Tnam = aname; Tnam1 = 0; #ifndef AFS_DARWIN80_ENV if (tvc) Ttvcr = VREFCOUNT(tvc); #endif #ifdef AFS_AIX_ENV if (tvc && VREFCOUNT_GT(tvc, 2) && tvc->opens > 0 && !(tvc->f.states & CUnlinked)) { #else if (tvc && VREFCOUNT_GT(tvc, 1) && tvc->opens > 0 && !(tvc->f.states & CUnlinked)) { #endif char *unlname = afs_newname(); ReleaseWriteLock(&adp->lock); if (tdc) ReleaseSharedLock(&tdc->lock); code = afsrename(adp, aname, adp, unlname, acred, &treq); Tnam1 = unlname; if (!code) { struct VenusFid *oldmvid = NULL; if (tvc->mvid) oldmvid = tvc->mvid; tvc->mvid = (struct VenusFid *)unlname; if (oldmvid) osi_FreeSmallSpace(oldmvid); crhold(acred); if (tvc->uncred) { crfree(tvc->uncred); } tvc->uncred = acred; tvc->f.states |= CUnlinked; /* if rename succeeded, remove should not */ ObtainWriteLock(&tvc->lock, 715); if (tvc->f.ddirty_flags & VDisconRemove) { tvc->f.ddirty_flags &= ~VDisconRemove; } ReleaseWriteLock(&tvc->lock); } else { osi_FreeSmallSpace(unlname); } if (tdc) afs_PutDCache(tdc); afs_PutVCache(tvc); } else { code = afsremove(adp, tdc, tvc, aname, acred, &treq); } done: afs_PutFakeStat(&fakestate); #ifndef AFS_DARWIN80_ENV /* we can't track by thread, it's not exported in the KPI; only do this on !macos */ osi_Assert(!WriteLocked(&adp->lock) || (adp->lock.pid_writer != MyPidxx)); #endif AFS_DISCON_UNLOCK(); return code; } /* afs_remunlink -- This tries to delete the file at the server after it has * been renamed when unlinked locally but now has been finally released. * * CAUTION -- may be called with avc unheld. */ int afs_remunlink(register struct vcache *avc, register int doit) { afs_ucred_t *cred; char *unlname; struct vcache *adp; struct vrequest treq; struct VenusFid dirFid; register struct dcache *tdc; afs_int32 code = 0; if (NBObtainWriteLock(&avc->lock, 423)) return 0; #if defined(AFS_DARWIN80_ENV) if (vnode_get(AFSTOV(avc))) { ReleaseWriteLock(&avc->lock); return 0; } #endif if (avc->mvid && (doit || (avc->f.states & CUnlinkedDel))) { if ((code = afs_InitReq(&treq, avc->uncred))) { ReleaseWriteLock(&avc->lock); } else { /* Must bump the refCount because GetVCache may block. * Also clear mvid so no other thread comes here if we block. */ unlname = (char *)avc->mvid; avc->mvid = NULL; cred = avc->uncred; avc->uncred = NULL; #if defined(AFS_DARWIN_ENV) && !defined(AFS_DARWIN80_ENV) VREF(AFSTOV(avc)); #else AFS_FAST_HOLD(avc); #endif /* We'll only try this once. If it fails, just release the vnode. * Clear after doing hold so that NewVCache doesn't find us yet. */ avc->f.states &= ~(CUnlinked | CUnlinkedDel); ReleaseWriteLock(&avc->lock); dirFid.Cell = avc->f.fid.Cell; dirFid.Fid.Volume = avc->f.fid.Fid.Volume; dirFid.Fid.Vnode = avc->f.parent.vnode; dirFid.Fid.Unique = avc->f.parent.unique; adp = afs_GetVCache(&dirFid, &treq, NULL, NULL); if (adp) { tdc = afs_FindDCache(adp, (afs_size_t) 0); ObtainWriteLock(&adp->lock, 159); if (tdc) ObtainSharedLock(&tdc->lock, 639); /* afsremove releases the adp & tdc locks, and does vn_rele(avc) */ code = afsremove(adp, tdc, avc, unlname, cred, &treq); afs_PutVCache(adp); } else { /* we failed - and won't be back to try again. */ afs_PutVCache(avc); } osi_FreeSmallSpace(unlname); crfree(cred); } } else { #if defined(AFS_DARWIN80_ENV) vnode_put(AFSTOV(avc)); #endif ReleaseWriteLock(&avc->lock); } return code; }
int osi_TryEvictVCache(struct vcache *avc, int *slept, int defersleep) { int code; struct dentry *dentry; struct inode *inode = AFSTOV(avc); struct list_head *cur, *head; /* First, see if we can evict the inode from the dcache */ if (defersleep && avc != afs_globalVp && VREFCOUNT(avc) > 1 && avc->opens == 0) { *slept = 1; ReleaseWriteLock(&afs_xvcache); AFS_GUNLOCK(); #if defined(HAVE_DCACHE_LOCK) spin_lock(&dcache_lock); head = &inode->i_dentry; restart: cur = head; while ((cur = cur->next) != head) { dentry = list_entry(cur, struct dentry, d_alias); if (d_unhashed(dentry)) continue; dget_locked(dentry); spin_unlock(&dcache_lock); if (d_invalidate(dentry) == -EBUSY) { dput(dentry); /* perhaps lock and try to continue? (use cur as head?) */ goto inuse; } dput(dentry); spin_lock(&dcache_lock); goto restart; } spin_unlock(&dcache_lock); #else /* HAVE_DCACHE_LOCK */ spin_lock(&inode->i_lock); head = &inode->i_dentry; restart: cur = head; while ((cur = cur->next) != head) { dentry = list_entry(cur, struct dentry, d_alias); spin_lock(&dentry->d_lock); if (d_unhashed(dentry)) { spin_unlock(&dentry->d_lock); continue; } spin_unlock(&dentry->d_lock); dget(dentry); spin_unlock(&inode->i_lock); if (d_invalidate(dentry) == -EBUSY) { dput(dentry); /* perhaps lock and try to continue? (use cur as head?) */ goto inuse; } dput(dentry); spin_lock(&inode->i_lock); goto restart; } spin_unlock(&inode->i_lock); #endif /* HAVE_DCACHE_LOCK */ inuse: AFS_GLOCK(); ObtainWriteLock(&afs_xvcache, 733); }
int SRXAFSCB_GetCE(struct rx_call *a_call, afs_int32 a_index, struct AFSDBCacheEntry *a_result) { int i; /*Loop variable */ struct vcache *tvc; /*Ptr to current cache entry */ int code; /*Return code */ XSTATS_DECLS; RX_AFS_GLOCK(); XSTATS_START_CMTIME(AFS_STATS_CM_RPCIDX_GETCE); AFS_STATCNT(SRXAFSCB_GetCE); for (i = 0; i < VCSIZE; i++) { for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) { if (a_index == 0) goto searchDone; a_index--; } /*Zip through current hash chain */ } /*Zip through hash chains */ searchDone: if (tvc == NULL) { /*Past EOF */ code = 1; goto fcnDone; } /* * Copy out the located entry. */ a_result->addr = afs_data_pointer_to_int32(tvc); a_result->cell = tvc->f.fid.Cell; a_result->netFid.Volume = tvc->f.fid.Fid.Volume; a_result->netFid.Vnode = tvc->f.fid.Fid.Vnode; a_result->netFid.Unique = tvc->f.fid.Fid.Unique; a_result->lock.waitStates = tvc->lock.wait_states; a_result->lock.exclLocked = tvc->lock.excl_locked; a_result->lock.readersReading = tvc->lock.readers_reading; a_result->lock.numWaiting = tvc->lock.num_waiting; #if defined(INSTRUMENT_LOCKS) a_result->lock.pid_last_reader = MyPidxx2Pid(tvc->lock.pid_last_reader); a_result->lock.pid_writer = MyPidxx2Pid(tvc->lock.pid_writer); a_result->lock.src_indicator = tvc->lock.src_indicator; #else /* On osf20 , the vcache does not maintain these three fields */ a_result->lock.pid_last_reader = 0; a_result->lock.pid_writer = 0; a_result->lock.src_indicator = 0; #endif /* INSTRUMENT_LOCKS */ #ifdef AFS_64BIT_CLIENT a_result->Length = (afs_int32) tvc->f.m.Length & 0xffffffff; #else /* AFS_64BIT_CLIENT */ a_result->Length = tvc->f.m.Length; #endif /* AFS_64BIT_CLIENT */ a_result->DataVersion = hgetlo(tvc->f.m.DataVersion); a_result->callback = afs_data_pointer_to_int32(tvc->callback); /* XXXX Now a pointer; change it XXXX */ a_result->cbExpires = tvc->cbExpires; if (tvc->f.states & CVInit) { a_result->refCount = 1; } else { #ifdef AFS_DARWIN80_ENV a_result->refCount = vnode_isinuse(AFSTOV(tvc),0)?1:0; /* XXX fix */ #else a_result->refCount = VREFCOUNT(tvc); #endif } a_result->opens = tvc->opens; a_result->writers = tvc->execsOrWriters; a_result->mvstat = tvc->mvstat; a_result->states = tvc->f.states; code = 0; /* * Return our results. */ fcnDone: XSTATS_END_TIME; RX_AFS_GUNLOCK(); return (code); } /*SRXAFSCB_GetCE */