/******************************************************************************/ /* */ /* X r d C m s C a c h e . c c */ /* */ /* (c) 2007 by the Board of Trustees of the Leland Stanford, Jr., University */ /* All Rights Reserved */ /* Produced by Andrew Hanushevsky for Stanford University under contract */ /* DE-AC02-76-SFO0515 with the Department of Energy */ /* */ /* This file is part of the XRootD software suite. */ /* */ /* XRootD is free software: you can redistribute it and/or modify it under */ /* the terms of the GNU Lesser General Public License as published by the */ /* Free Software Foundation, either version 3 of the License, or (at your */ /* option) any later version. */ /* */ /* XRootD is distributed in the hope that it will be useful, but WITHOUT */ /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */ /* License for more details. */ /* */ /* You should have received a copy of the GNU Lesser General Public License */ /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */ /* COPYING (GPL license). If not, see . */ /* */ /* The copyright holder's institutional names and contributor's names may not */ /* be used to endorse or promote products derived from this software without */ /* specific prior written permission of the institution or contributor. */ /******************************************************************************/ #include #include #include "XrdCms/XrdCmsCache.hh" #include "XrdCms/XrdCmsRRQ.hh" #include "XrdCms/XrdCmsTrace.hh" #include "XrdSys/XrdSysTimer.hh" #include "Xrd/XrdJob.hh" #include "Xrd/XrdScheduler.hh" namespace XrdCms { extern XrdScheduler *Sched; } using namespace XrdCms; /******************************************************************************/ /* G l o b a l s */ /******************************************************************************/ XrdCmsCache XrdCms::Cache; /******************************************************************************/ /* L o c a l C l a s s e s */ /******************************************************************************/ class XrdCmsCacheJob : XrdJob { public: void DoIt() {Cache.Recycle(myList); delete this;} XrdCmsCacheJob(XrdCmsKeyItem *List) : XrdJob("cache scrubber"), myList(List) {} ~XrdCmsCacheJob() {} private: XrdCmsKeyItem *myList; }; /******************************************************************************/ /* E x t e r n a l T h r e a d I n t e r f a c e s */ /******************************************************************************/ void *XrdCmsStartTickTock(void *carg) {XrdCmsCache *myCache = (XrdCmsCache *)carg; return myCache->TickTock(); } /******************************************************************************/ /* P u b l i c C a c h e M a n i p u l a t i o n M e t h o d s */ /******************************************************************************/ /******************************************************************************/ /* Public A d d F i l e */ /******************************************************************************/ // This method insert or updates information about a path in the cache. // Key was found: Location information is updated depending on mask // mask == 0 Indicates that the information is being refreshed. // Location information is nullified. The update deadline is set // QDelay seconds in the future. The entry window is set to the // current window to be held for a full fxhold period. // mask != 0 Indicates that some location information is now known. // Location information is updated according to the mask. // For a r/w location, the deadline is satisfied and all // callbacks are dispatched. For an r/o location the deadline // is satisfied if no r/w callback is pending. Any r/o // callback is dispatched. The Info object is ignored. // Key not found: A selective addition occurs, depending on Sel.Opts // Opts !Advisory: The entry is added to the cache with location information // set as passed (usually 0). The update deadline is us set to // DLTtime seconds in the future. The entry window is set // to the current window. // Opts Advisory: The call is ignored since we do not keep information about // paths that were never asked for. // Returns True If this is the first time location information was added // to the entry. // Returns False Otherwise. int XrdCmsCache::AddFile(XrdCmsSelect &Sel, SMask_t mask) { XrdCmsKeyItem *iP; SMask_t xmask; int isrw = (Sel.Opts & XrdCmsSelect::Write), isnew = 0; // Serialize processing // myMutex.Lock(); // Check for fast path processing // if ( !(iP = Sel.Path.TODRef) || !(iP->Key.Equiv(Sel.Path))) if ((iP = Sel.Path.TODRef = CTable.Find(Sel.Path))) Sel.Path.Ref = iP->Key.Ref; // Add/Modify the entry // if (iP) {if (!mask) {iP->Loc.deadline = QDelay + time(0); iP->Loc.lifeline = nilTMO + iP->Loc.deadline; iP->Loc.hfvec = 0; iP->Loc.pfvec = 0; iP->Loc.qfvec = 0; iP->Loc.TOD_B = BClock; iP->Key.TOD = Tock; } else { xmask = iP->Loc.pfvec; if (Sel.Opts & XrdCmsSelect::Pending) iP->Loc.pfvec |= mask; else iP->Loc.pfvec &= ~mask; isnew = (iP->Loc.hfvec == 0) || (iP->Loc.pfvec != xmask); iP->Loc.hfvec |= mask; iP->Loc.qfvec &= ~mask; if (isrw) {iP->Loc.deadline = 0; if (iP->Loc.roPend || iP->Loc.rwPend) Dispatch(Sel, iP, iP->Loc.roPend, iP->Loc.rwPend); } else {if (!iP->Loc.rwPend) iP->Loc.deadline = 0; if (iP->Loc.roPend) Dispatch(Sel, iP, iP->Loc.roPend, 0); } } } else if (!(Sel.Opts & XrdCmsSelect::Advisory)) {Sel.Path.TOD = Tock; if ((iP = CTable.Add(Sel.Path))) {iP->Loc.pfvec = (Sel.Opts&XrdCmsSelect::Pending?mask:0); iP->Loc.hfvec = mask; iP->Loc.TOD_B = BClock; iP->Loc.qfvec = 0; iP->Loc.deadline = QDelay + time(0); iP->Loc.lifeline = nilTMO + iP->Loc.deadline; Sel.Path.Ref = iP->Key.Ref; Sel.Path.TODRef = iP; isnew = 1; } } // All done // myMutex.UnLock(); return isnew; } /******************************************************************************/ /* Public D e l F i l e */ /******************************************************************************/ // This method removes location information from existing valid entries. If an // existing valid entry is found, based on Sel.Opts the following occurs: // Opts Advisory only locate information is removed. // Opts !Advisory if the entry has no location information it is removed from // the cache, if possible. // TRUE is returned if the entry was valid but location information was cleared. // Otherwise, FALSE is returned. int XrdCmsCache::DelFile(XrdCmsSelect &Sel, SMask_t mask) { XrdCmsKeyItem *iP; int gone4good; // Lock the hash table // myMutex.Lock(); // Look up the entry and remove server // if ((iP = CTable.Find(Sel.Path))) {iP->Loc.hfvec &= ~mask; iP->Loc.pfvec &= ~mask; if ((gone4good = (iP->Loc.hfvec == 0))) {if (nilTMO) iP->Loc.lifeline = nilTMO + time(0); if (!(Sel.Opts & XrdCmsSelect::Advisory) && XrdCmsKeyItem::Unload(iP) && !CTable.Recycle(iP)) Say.Emsg("DelFile", "Delete failed for", iP->Key.Val); } } else gone4good = 0; // All done // myMutex.UnLock(); return gone4good; } /******************************************************************************/ /* Public G e t F i l e */ /******************************************************************************/ // This method looks up entries in the cache. An "entry not found" condition // holds is the entry was found but is marked as deleted. // Entry was found: Location information is passed bask. If the update deadline // has passed, it is nullified and 1 is returned. Otherwise, // -1 is returned indicating a query is in progress. // Entry not found: FALSE is returned. int XrdCmsCache::GetFile(XrdCmsSelect &Sel, SMask_t mask) { XrdCmsKeyItem *iP; SMask_t bVec; int retc; // Lock the hash table // myMutex.Lock(); // Look up the entry and return location information // if ((iP = CTable.Find(Sel.Path))) {if ((bVec = (iP->Loc.TOD_B < BClock ? getBVec(iP->Key.TOD, iP->Loc.TOD_B) & mask : 0))) {iP->Loc.hfvec &= ~bVec; iP->Loc.pfvec &= ~bVec; iP->Loc.qfvec &= ~mask; iP->Loc.deadline = QDelay + time(0); iP->Loc.lifeline = nilTMO + iP->Loc.deadline; retc = -1; } else if (iP->Loc.deadline) if (iP->Loc.deadline > time(0)) retc = -1; else {iP->Loc.deadline = 0; retc = 1;} else retc = 1; if (nilTMO && retc == 1 && iP->Loc.hfvec == 0 && iP->Loc.lifeline <= time(0)) retc = 0; Sel.Vec.hf = okVec & iP->Loc.hfvec; Sel.Vec.pf = okVec & iP->Loc.pfvec; Sel.Vec.bf = okVec & (bVec | iP->Loc.qfvec); iP->Loc.qfvec = 0; Sel.Path.Ref = iP->Key.Ref; } else retc = 0; // All done // myMutex.UnLock(); Sel.Path.TODRef = iP; return retc; } /******************************************************************************/ /* Public U n k F i l e */ /******************************************************************************/ int XrdCmsCache::UnkFile(XrdCmsSelect &Sel, SMask_t mask) { EPNAME("UnkFile"); XrdCmsKeyItem *iP; // Make sure we have the proper information. If so, lock the hash table // myMutex.Lock(); // Look up the entry and if valid update the unqueried vector. Note that // this method may only be called after GetFile() or AddFile() for a new entry // if ((iP = Sel.Path.TODRef)) {if (iP->Key.Equiv(Sel.Path)) iP->Loc.qfvec = mask; else iP = 0; } // Return result // myMutex.UnLock(); DEBUG("rc=" <<(iP ? 1 : 0) <<" path=" <Key.Equiv(Sel.Path))) retc = DLTime; else if (iP->Loc.hfvec != mask) retc = 1; else {Now = time(0); retc = 0; if (iP->Loc.deadline && iP->Loc.deadline <= Now) iP->Loc.deadline = DLTime + Now; Add2Q(Sel.InfoP, iP, Sel.Opts); } // Return result // myMutex.UnLock(); DEBUG("rc=" <Recycle(); // All done // return 1; } /******************************************************************************/ /* public T i c k T o c k */ /******************************************************************************/ void *XrdCmsCache::TickTock() { XrdCmsKeyItem *iP; // Simply adjust the clock and trim old entries // do {XrdSysTimer::Snooze(Tick); myMutex.Lock(); Tock = (Tock+1) & XrdCmsKeyItem::TickMask; Bhistory[Tock].Start = Bhistory[Tock].End = 0; iP = XrdCmsKeyItem::Unload(Tock); myMutex.UnLock(); if (iP) Sched->Schedule((XrdJob *)new XrdCmsCacheJob(iP)); } while(1); // Keep compiler happy // return (void *)0; } /******************************************************************************/ /* P r i v a t e M e t h o d s */ /******************************************************************************/ /******************************************************************************/ /* A d d 2 Q */ /******************************************************************************/ void XrdCmsCache::Add2Q(XrdCmsRRQInfo *Info, XrdCmsKeyItem *iP, int selOpts) { bool isrw = (selOpts & XrdCmsSelect::Write) != 0; short Slot = (isrw ? iP->Loc.rwPend : iP->Loc.roPend); // Add the request to the appropriate pending queue // Info->Key = iP; Info->isRW= isrw; Info->ifOP= (selOpts & XrdCmsSelect::ifWant); if (!(Slot = RRQ.Add(Slot, Info))) Info->Key = 0; else if (isrw) iP->Loc.rwPend = Slot; else iP->Loc.roPend = Slot; } /******************************************************************************/ /* D i s p a t c h */ /******************************************************************************/ void XrdCmsCache::Dispatch(XrdCmsSelect &Sel, XrdCmsKeyItem *iP, short roQ, short rwQ) { // Dispatching shared-everything nodes is very different from shared-nothing // since one ready node means all are ready and we can use any one of them. // The current list of nodes must is provided by the caller adding the entry. // Note that the minimum number of nodes will always be set to 0 via config. // if (isDFS) {if (roQ && RRQ.Ready(roQ, iP, Sel.Vec.hf, Sel.Vec.pf & Sel.Vec.hf)) iP->Loc.roPend = 0; if((rwQ && Sel.Vec.wf) && RRQ.Ready(rwQ, iP, Sel.Vec.wf, Sel.Vec.pf & Sel.Vec.wf)) iP->Loc.rwPend = 0; return; } // Disptaching shared-nothing nodes is a one-shot affair. Only one node becomes // ready at a time and we can immediately disptach that node unless we need to // wait for more nodes to respond. // if (roQ && RRQ.Ready(roQ, iP, iP->Loc.hfvec, iP->Loc.pfvec)) iP->Loc.roPend = 0; if (rwQ && RRQ.Ready(rwQ, iP, iP->Loc.hfvec, iP->Loc.pfvec)) iP->Loc.rwPend = 0; } /******************************************************************************/ /* g e t B V e c */ /******************************************************************************/ SMask_t XrdCmsCache::getBVec(unsigned int TODa, unsigned int &TODb) { EPNAME("getBVec"); SMask_t BVec(0); long long i; // See if we can use a previously calculated bVec // if (Bhistory[TODa].End == BClock && Bhistory[TODa].Start <= TODb) {Bhits++; TODb = BClock; return Bhistory[TODa].Vec;} // Calculate the new vector // for (i = 0; i <= vecHi; i++) if (TODb < Bounced[i]) BVec |= 1ULL << i; Bhistory[TODa].Vec = BVec; Bhistory[TODa].Start = TODb; Bhistory[TODa].End = BClock; TODb = BClock; Bmiss++; if (!(Bmiss & 0xff)) DEBUG("hits=" <