XRootD
XrdPfc.cc
Go to the documentation of this file.
1 //----------------------------------------------------------------------------------
2 // Copyright (c) 2014 by Board of Trustees of the Leland Stanford, Jr., University
3 // Author: Alja Mrak-Tadel, Matevz Tadel, Brian Bockelman
4 //----------------------------------------------------------------------------------
5 // XRootD is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU Lesser General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // XRootD is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with XRootD. If not, see <http://www.gnu.org/licenses/>.
17 //----------------------------------------------------------------------------------
18 
19 #include <fcntl.h>
20 #include <sstream>
21 #include <algorithm>
22 #include <sys/statvfs.h>
23 
24 #include "XrdCl/XrdClConstants.hh"
25 #include "XrdCl/XrdClURL.hh"
26 
27 #include "XrdOuc/XrdOucEnv.hh"
28 #include "XrdOuc/XrdOucUtils.hh"
30 
31 #include "XrdSys/XrdSysTimer.hh"
32 #include "XrdSys/XrdSysTrace.hh"
33 #include "XrdSys/XrdSysXAttr.hh"
34 
36 
37 #include "XrdOss/XrdOss.hh"
38 
39 #include "XrdPfc.hh"
40 #include "XrdPfcTrace.hh"
41 #include "XrdPfcFSctl.hh"
42 #include "XrdPfcInfo.hh"
43 #include "XrdPfcIOFile.hh"
44 #include "XrdPfcIOFileBlock.hh"
45 #include "XrdPfcResourceMonitor.hh"
46 
48 
49 using namespace XrdPfc;
50 
51 Cache *Cache::m_instance = nullptr;
52 XrdScheduler *Cache::schedP = nullptr;
53 
54 
56 {
58  return 0;
59 }
60 
62 {
64  return 0;
65 }
66 
67 void *PrefetchThread(void*)
68 {
70  return 0;
71 }
72 
73 //==============================================================================
74 
75 extern "C"
76 {
78  const char *config_filename,
79  const char *parameters,
80  XrdOucEnv *env)
81 {
82  XrdSysError err(logger, "");
83  err.Say("++++++ Proxy file cache initialization started.");
84 
85  if ( ! env ||
86  ! (XrdPfc::Cache::schedP = (XrdScheduler*) env->GetPtr("XrdScheduler*")))
87  {
90  }
91 
92  Cache &instance = Cache::CreateInstance(logger, env);
93 
94  if (! instance.Config(config_filename, parameters))
95  {
96  err.Say("Config Proxy file cache initialization failed.");
97  return 0;
98  }
99  err.Say("++++++ Proxy file cache initialization completed.");
100 
101  {
102  pthread_t tid;
103 
104  XrdSysThread::Run(&tid, ResourceMonitorThread, 0, 0, "XrdPfc ResourceMonitor");
105 
106  for (int wti = 0; wti < instance.RefConfiguration().m_wqueue_threads; ++wti)
107  {
108  XrdSysThread::Run(&tid, ProcessWriteTaskThread, 0, 0, "XrdPfc WriteTasks ");
109  }
110 
111  if (instance.RefConfiguration().m_prefetch_max_blocks > 0)
112  {
113  XrdSysThread::Run(&tid, PrefetchThread, 0, 0, "XrdPfc Prefetch ");
114  }
115  }
116 
117  XrdPfcFSctl* pfcFSctl = new XrdPfcFSctl(instance, logger);
118  env->PutPtr("XrdFSCtl_PC*", pfcFSctl);
119 
120  return &instance;
121 }
122 }
123 
124 //==============================================================================
125 
127 {
128  assert (m_instance == 0);
129  m_instance = new Cache(logger, env);
130  return *m_instance;
131 }
132 
133  Cache& Cache::GetInstance() { return *m_instance; }
134 const Cache& Cache::TheOne() { return *m_instance; }
135 const Configuration& Cache::Conf() { return m_instance->RefConfiguration(); }
136  ResourceMonitor& Cache::ResMon() { return m_instance->RefResMon(); }
137 
139 {
140  if (! m_decisionpoints.empty())
141  {
142  XrdCl::URL url(io->Path());
143  std::string filename = url.GetPath();
144  std::vector<Decision*>::const_iterator it;
145  for (it = m_decisionpoints.begin(); it != m_decisionpoints.end(); ++it)
146  {
147  XrdPfc::Decision *d = *it;
148  if (! d) continue;
149  if (! d->Decide(filename, *m_oss))
150  {
151  return false;
152  }
153  }
154  }
155 
156  return true;
157 }
158 
160  XrdOucCache("pfc"),
161  m_env(env),
162  m_log(logger, "XrdPfc_"),
163  m_trace(new XrdSysTrace("XrdPfc", logger)),
164  m_traceID("Cache"),
165  m_oss(0),
166  m_gstream(0),
167  m_purge_pin(0),
168  m_prefetch_condVar(0),
169  m_prefetch_enabled(false),
170  m_RAM_used(0),
171  m_RAM_write_queue(0),
172  m_RAM_std_size(0),
173  m_isClient(false),
174  m_active_cond(0)
175 {
176  // Default log level is Warning.
177  m_trace->What = 2;
178 }
179 
181 {
182  const char* tpfx = "Attach() ";
183 
184  if (Cache::GetInstance().Decide(io))
185  {
186  TRACE(Info, tpfx << obfuscateAuth(io->Path()));
187 
188  IO *cio;
189 
190  if (Cache::GetInstance().RefConfiguration().m_hdfsmode)
191  {
192  cio = new IOFileBlock(io, *this);
193  }
194  else
195  {
196  IOFile *iof = new IOFile(io, *this);
197 
198  if ( ! iof->HasFile())
199  {
200  delete iof;
201  // TODO - redirect instead. But this is kind of an awkward place for it.
202  // errno is set during IOFile construction.
203  TRACE(Error, tpfx << "Failed opening local file, falling back to remote access " << io->Path());
204  return io;
205  }
206 
207  cio = iof;
208  }
209 
210  TRACE_PC(Debug, const char* loc = io->Location(), tpfx << io->Path() << " location: " <<
211  ((loc && loc[0] != 0) ? loc : "<deferred open>"));
212 
213  return cio;
214  }
215  else
216  {
217  TRACE(Info, tpfx << "decision decline " << io->Path());
218  }
219  return io;
220 }
221 
222 void Cache::AddWriteTask(Block* b, bool fromRead)
223 {
224  TRACE(Dump, "AddWriteTask() offset=" << b->m_offset << ". file " << b->get_file()->GetLocalPath());
225 
226  {
227  XrdSysMutexHelper lock(&m_RAM_mutex);
228  m_RAM_write_queue += b->get_size();
229  }
230 
231  m_writeQ.condVar.Lock();
232  if (fromRead)
233  m_writeQ.queue.push_back(b);
234  else
235  m_writeQ.queue.push_front(b);
236  m_writeQ.size++;
237  m_writeQ.condVar.Signal();
238  m_writeQ.condVar.UnLock();
239 }
240 
242 {
243  std::list<Block*> removed_blocks;
244  long long sum_size = 0;
245 
246  m_writeQ.condVar.Lock();
247  std::list<Block*>::iterator i = m_writeQ.queue.begin();
248  while (i != m_writeQ.queue.end())
249  {
250  if ((*i)->m_file == file)
251  {
252  TRACE(Dump, "Remove entries for " << (void*)(*i) << " path " << file->lPath());
253  std::list<Block*>::iterator j = i++;
254  removed_blocks.push_back(*j);
255  sum_size += (*j)->get_size();
256  m_writeQ.queue.erase(j);
257  --m_writeQ.size;
258  }
259  else
260  {
261  ++i;
262  }
263  }
264  m_writeQ.condVar.UnLock();
265 
266  {
267  XrdSysMutexHelper lock(&m_RAM_mutex);
268  m_RAM_write_queue -= sum_size;
269  }
270 
271  file->BlocksRemovedFromWriteQ(removed_blocks);
272 }
273 
275 {
276  std::vector<Block*> blks_to_write(m_configuration.m_wqueue_blocks);
277 
278  while (true)
279  {
280  m_writeQ.condVar.Lock();
281  while (m_writeQ.size == 0)
282  {
283  m_writeQ.condVar.Wait();
284  }
285 
286  // MT -- optimize to pop several blocks if they are available (or swap the list).
287  // This makes sense especially for smallish block sizes.
288 
289  int n_pushed = std::min(m_writeQ.size, m_configuration.m_wqueue_blocks);
290  long long sum_size = 0;
291 
292  for (int bi = 0; bi < n_pushed; ++bi)
293  {
294  Block* block = m_writeQ.queue.front();
295  m_writeQ.queue.pop_front();
296  m_writeQ.writes_between_purges += block->get_size();
297  sum_size += block->get_size();
298 
299  blks_to_write[bi] = block;
300 
301  TRACE(Dump, "ProcessWriteTasks for block " << (void*)(block) << " path " << block->m_file->lPath());
302  }
303  m_writeQ.size -= n_pushed;
304 
305  m_writeQ.condVar.UnLock();
306 
307  {
308  XrdSysMutexHelper lock(&m_RAM_mutex);
309  m_RAM_write_queue -= sum_size;
310  }
311 
312  for (int bi = 0; bi < n_pushed; ++bi)
313  {
314  Block* block = blks_to_write[bi];
315 
316  block->m_file->WriteBlockToDisk(block);
317  }
318  }
319 }
320 
322 {
323  // Called from ResourceMonitor for an alternative estimation of disk writes.
324  XrdSysCondVarHelper lock(&m_writeQ.condVar);
325  long long ret = m_writeQ.writes_between_purges;
326  m_writeQ.writes_between_purges = 0;
327  return ret;
328 }
329 
330 //==============================================================================
331 
332 char* Cache::RequestRAM(long long size)
333 {
334  static const size_t s_block_align = sysconf(_SC_PAGESIZE);
335 
336  bool std_size = (size == m_configuration.m_bufferSize);
337 
338  m_RAM_mutex.Lock();
339 
340  long long total = m_RAM_used + size;
341 
342  if (total <= m_configuration.m_RamAbsAvailable)
343  {
344  m_RAM_used = total;
345  if (std_size && m_RAM_std_size > 0)
346  {
347  char *buf = m_RAM_std_blocks.back();
348  m_RAM_std_blocks.pop_back();
349  --m_RAM_std_size;
350 
351  m_RAM_mutex.UnLock();
352 
353  return buf;
354  }
355  else
356  {
357  m_RAM_mutex.UnLock();
358  char *buf;
359  if (posix_memalign((void**) &buf, s_block_align, (size_t) size))
360  {
361  // Report out of mem? Probably should report it at least the first time,
362  // then periodically.
363  return 0;
364  }
365  return buf;
366  }
367  }
368  m_RAM_mutex.UnLock();
369  return 0;
370 }
371 
372 void Cache::ReleaseRAM(char* buf, long long size)
373 {
374  bool std_size = (size == m_configuration.m_bufferSize);
375  {
376  XrdSysMutexHelper lock(&m_RAM_mutex);
377 
378  m_RAM_used -= size;
379 
380  if (std_size && m_RAM_std_size < m_configuration.m_RamKeepStdBlocks)
381  {
382  m_RAM_std_blocks.push_back(buf);
383  ++m_RAM_std_size;
384  return;
385  }
386  }
387  free(buf);
388 }
389 
390 File* Cache::GetFile(const std::string& path, IO* io, long long off, long long filesize)
391 {
392  // Called from virtual IOFile constructor.
393 
394  TRACE(Debug, "GetFile " << path << ", io " << io);
395 
396  ActiveMap_i it;
397 
398  {
399  XrdSysCondVarHelper lock(&m_active_cond);
400 
401  while (true)
402  {
403  it = m_active.find(path);
404 
405  // File is not open or being opened. Mark it as being opened and
406  // proceed to opening it outside of while loop.
407  if (it == m_active.end())
408  {
409  it = m_active.insert(std::make_pair(path, (File*) 0)).first;
410  break;
411  }
412 
413  if (it->second != 0)
414  {
415  it->second->AddIO(io);
416  inc_ref_cnt(it->second, false, true);
417 
418  return it->second;
419  }
420  else
421  {
422  // Wait for some change in m_active, then recheck.
423  m_active_cond.Wait();
424  }
425  }
426  }
427 
428  // This is always true, now that IOFileBlock is unsupported.
429  if (filesize == 0)
430  {
431  struct stat st;
432  int res = io->Fstat(st);
433  if (res < 0) {
434  errno = res;
435  TRACE(Error, "GetFile, could not get valid stat");
436  } else if (res > 0) {
437  errno = ENOTSUP;
438  TRACE(Error, "GetFile, stat returned positive value, this should NOT happen here");
439  } else {
440  filesize = st.st_size;
441  }
442  }
443 
444  File *file = 0;
445 
446  if (filesize >= 0)
447  {
448  file = File::FileOpen(path, off, filesize);
449  }
450 
451  {
452  XrdSysCondVarHelper lock(&m_active_cond);
453 
454  if (file)
455  {
456  inc_ref_cnt(file, false, true);
457  it->second = file;
458 
459  file->AddIO(io);
460  }
461  else
462  {
463  m_active.erase(it);
464  }
465 
466  m_active_cond.Broadcast();
467  }
468 
469  return file;
470 }
471 
473 {
474  // Called from virtual IO::DetachFinalize.
475 
476  TRACE(Debug, "ReleaseFile " << f->GetLocalPath() << ", io " << io);
477 
478  {
479  XrdSysCondVarHelper lock(&m_active_cond);
480 
481  f->RemoveIO(io);
482  }
483  dec_ref_cnt(f, true);
484 }
485 
486 
487 namespace
488 {
489 
490 class DiskSyncer : public XrdJob
491 {
492 private:
493  File *m_file;
494  bool m_high_debug;
495 
496 public:
497  DiskSyncer(File *f, bool high_debug, const char *desc = "") :
498  XrdJob(desc),
499  m_file(f),
500  m_high_debug(high_debug)
501  {}
502 
503  void DoIt()
504  {
505  m_file->Sync();
506  Cache::GetInstance().FileSyncDone(m_file, m_high_debug);
507  delete this;
508  }
509 };
510 
511 
512 class CommandExecutor : public XrdJob
513 {
514 private:
515  std::string m_command_url;
516 
517 public:
518  CommandExecutor(const std::string& command, const char *desc = "") :
519  XrdJob(desc),
520  m_command_url(command)
521  {}
522 
523  void DoIt()
524  {
525  Cache::GetInstance().ExecuteCommandUrl(m_command_url);
526  delete this;
527  }
528 };
529 
530 }
531 
532 //==============================================================================
533 
534 void Cache::schedule_file_sync(File* f, bool ref_cnt_already_set, bool high_debug)
535 {
536  DiskSyncer* ds = new DiskSyncer(f, high_debug);
537 
538  if ( ! ref_cnt_already_set) inc_ref_cnt(f, true, high_debug);
539 
540  schedP->Schedule(ds);
541 }
542 
543 void Cache::FileSyncDone(File* f, bool high_debug)
544 {
545  dec_ref_cnt(f, high_debug);
546 }
547 
548 void Cache::inc_ref_cnt(File* f, bool lock, bool high_debug)
549 {
550  // called from GetFile() or SheduleFileSync();
551 
552  int tlvl = high_debug ? TRACE_Debug : TRACE_Dump;
553 
554  if (lock) m_active_cond.Lock();
555  int rc = f->inc_ref_cnt();
556  if (lock) m_active_cond.UnLock();
557 
558  TRACE_INT(tlvl, "inc_ref_cnt " << f->GetLocalPath() << ", cnt at exit = " << rc);
559 }
560 
561 void Cache::dec_ref_cnt(File* f, bool high_debug)
562 {
563  // NOT under active lock.
564  // Called from ReleaseFile(), DiskSync callback and stat-like functions.
565 
566  int tlvl = high_debug ? TRACE_Debug : TRACE_Dump;
567  int cnt;
568 
569  {
570  XrdSysCondVarHelper lock(&m_active_cond);
571 
572  cnt = f->get_ref_cnt();
573  TRACE_INT(tlvl, "dec_ref_cnt " << f->GetLocalPath() << ", cnt at entry = " << cnt);
574 
575  if (f->is_in_emergency_shutdown())
576  {
577  // In this case file has been already removed from m_active map and
578  // does not need to be synced.
579 
580  if (cnt == 1)
581  {
582  TRACE_INT(tlvl, "dec_ref_cnt " << f->GetLocalPath() << " is in shutdown, ref_cnt = " << cnt
583  << " -- deleting File object without further ado");
584  delete f;
585  }
586  else
587  {
588  TRACE_INT(tlvl, "dec_ref_cnt " << f->GetLocalPath() << " is in shutdown, ref_cnt = " << cnt
589  << " -- waiting");
590  }
591  return;
592  }
593  if (cnt > 1)
594  {
595  f->dec_ref_cnt();
596  return;
597  }
598  }
599 
600  if (cnt == 1)
601  {
602  if (f->FinalizeSyncBeforeExit())
603  {
604  // Note, here we "reuse" the existing reference count for the
605  // final sync.
606 
607  TRACE(Debug, "dec_ref_cnt " << f->GetLocalPath() << ", scheduling final sync");
608  schedule_file_sync(f, true, true);
609  return;
610  }
611  }
612 
613  bool finished_p = false;
614  {
615  XrdSysCondVarHelper lock(&m_active_cond);
616 
617  cnt = f->dec_ref_cnt();
618  TRACE_INT(tlvl, "dec_ref_cnt " << f->GetLocalPath() << ", cnt after sync_check and dec_ref_cnt = " << cnt);
619  if (cnt == 0)
620  {
621  ActiveMap_i it = m_active.find(f->GetLocalPath());
622  m_active.erase(it);
623 
624  finished_p = true;
625  }
626  }
627  if (finished_p)
628  {
629  if (m_gstream)
630  {
631  const Stats &st = f->RefStats();
632  const Info::AStat *as = f->GetLastAccessStats();
633 
634  char buf[4096];
635  int len = snprintf(buf, 4096, "{\"event\":\"file_close\","
636  "\"lfn\":\"%s\",\"size\":%lld,\"blk_size\":%d,\"n_blks\":%d,\"n_blks_done\":%d,"
637  "\"access_cnt\":%lu,\"attach_t\":%lld,\"detach_t\":%lld,\"remotes\":%s,"
638  "\"b_hit\":%lld,\"b_miss\":%lld,\"b_bypass\":%lld,"
639  "\"b_todisk\":%lld,\"b_prefetch\":%lld,\"n_cks_errs\":%d}",
640  f->GetLocalPath().c_str(), f->GetFileSize(), f->GetBlockSize(),
641  f->GetNBlocks(), f->GetNDownloadedBlocks(),
642  (unsigned long) f->GetAccessCnt(), (long long) as->AttachTime, (long long) as->DetachTime,
643  f->GetRemoteLocations().c_str(),
644  as->BytesHit, as->BytesMissed, as->BytesBypassed,
646  );
647  bool suc = false;
648  if (len < 4096)
649  {
650  suc = m_gstream->Insert(buf, len + 1);
651  }
652  if ( ! suc)
653  {
654  TRACE(Error, "Failed g-stream insertion of file_close record, len=" << len);
655  }
656  }
657 
658  delete f;
659  }
660 }
661 
662 bool Cache::IsFileActiveOrPurgeProtected(const std::string& path) const
663 {
664  XrdSysCondVarHelper lock(&m_active_cond);
665 
666  return m_active.find(path) != m_active.end() ||
667  m_purge_delay_set.find(path) != m_purge_delay_set.end();
668 }
669 
671 {
672  XrdSysCondVarHelper lock(&m_active_cond);
673  m_purge_delay_set.clear();
674 }
675 
676 //==============================================================================
677 //=== PREFETCH
678 //==============================================================================
679 
681 {
682  // Can be called with other locks held.
683 
684  if ( ! m_prefetch_enabled)
685  {
686  return;
687  }
688 
689  m_prefetch_condVar.Lock();
690  m_prefetchList.push_back(file);
691  m_prefetch_condVar.Signal();
692  m_prefetch_condVar.UnLock();
693 }
694 
695 
697 {
698  // Can be called with other locks held.
699 
700  if ( ! m_prefetch_enabled)
701  {
702  return;
703  }
704 
705  m_prefetch_condVar.Lock();
706  for (PrefetchList::iterator it = m_prefetchList.begin(); it != m_prefetchList.end(); ++it)
707  {
708  if (*it == file)
709  {
710  m_prefetchList.erase(it);
711  break;
712  }
713  }
714  m_prefetch_condVar.UnLock();
715 }
716 
717 
719 {
720  m_prefetch_condVar.Lock();
721  while (m_prefetchList.empty())
722  {
723  m_prefetch_condVar.Wait();
724  }
725 
726  // std::sort(m_prefetchList.begin(), m_prefetchList.end(), myobject);
727 
728  size_t l = m_prefetchList.size();
729  int idx = rand() % l;
730  File* f = m_prefetchList[idx];
731 
732  m_prefetch_condVar.UnLock();
733  return f;
734 }
735 
736 
738 {
739  const long long limit_RAM = m_configuration.m_RamAbsAvailable * 7 / 10;
740 
741  while (true)
742  {
743  m_RAM_mutex.Lock();
744  bool doPrefetch = (m_RAM_used < limit_RAM);
745  m_RAM_mutex.UnLock();
746 
747  if (doPrefetch)
748  {
750  f->Prefetch();
751  }
752  else
753  {
755  }
756  }
757 }
758 
759 
760 //==============================================================================
761 //=== Virtuals from XrdOucCache
762 //==============================================================================
763 
764 //------------------------------------------------------------------------------
778 
779 int Cache::LocalFilePath(const char *curl, char *buff, int blen,
780  LFP_Reason why, bool forall)
781 {
782  static const mode_t groupReadable = S_IRUSR | S_IWUSR | S_IRGRP;
783  static const mode_t worldReadable = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
784  static const char *lfpReason[] = { "ForAccess", "ForInfo", "ForPath" };
785 
786  TRACE(Debug, "LocalFilePath '" << curl << "', why=" << lfpReason[why]);
787 
788  if (buff && blen > 0) buff[0] = 0;
789 
790  XrdCl::URL url(curl);
791  std::string f_name = url.GetPath();
792  std::string i_name = f_name + Info::s_infoExtension;
793 
794  if (why == ForPath)
795  {
796  int ret = m_oss->Lfn2Pfn(f_name.c_str(), buff, blen);
797  TRACE(Info, "LocalFilePath '" << curl << "', why=" << lfpReason[why] << " -> " << ret);
798  return ret;
799  }
800 
801  {
802  XrdSysCondVarHelper lock(&m_active_cond);
803  m_purge_delay_set.insert(f_name);
804  }
805 
806  struct stat sbuff, sbuff2;
807  if (m_oss->Stat(f_name.c_str(), &sbuff) == XrdOssOK &&
808  m_oss->Stat(i_name.c_str(), &sbuff2) == XrdOssOK)
809  {
810  if (S_ISDIR(sbuff.st_mode))
811  {
812  TRACE(Info, "LocalFilePath '" << curl << "', why=" << lfpReason[why] << " -> EISDIR");
813  return -EISDIR;
814  }
815  else
816  {
817  bool read_ok = false;
818  bool is_complete = false;
819 
820  // Lock and check if the file is active. If NOT, keep the lock
821  // and add dummy access after successful reading of info file.
822  // If it IS active, just release the lock, this ongoing access will
823  // assure the file continues to exist.
824 
825  // XXXX How can I just loop over the cinfo file when active?
826  // Can I not get is_complete from the existing file?
827  // Do I still want to inject access record?
828  // Oh, it writes only if not active .... still let's try to use existing File.
829 
830  m_active_cond.Lock();
831 
832  bool is_active = m_active.find(f_name) != m_active.end();
833 
834  if (is_active) m_active_cond.UnLock();
835 
836  XrdOssDF* infoFile = m_oss->newFile(m_configuration.m_username.c_str());
837  XrdOucEnv myEnv;
838  int res = infoFile->Open(i_name.c_str(), O_RDWR, 0600, myEnv);
839  if (res >= 0)
840  {
841  Info info(m_trace, 0);
842  if (info.Read(infoFile, i_name.c_str()))
843  {
844  read_ok = true;
845 
846  is_complete = info.IsComplete();
847 
848  // Add full-size access if reason is for access.
849  if ( ! is_active && is_complete && why == ForAccess)
850  {
851  info.WriteIOStatSingle(info.GetFileSize());
852  info.Write(infoFile, i_name.c_str());
853  }
854  }
855  infoFile->Close();
856  }
857  delete infoFile;
858 
859  if ( ! is_active) m_active_cond.UnLock();
860 
861  if (read_ok)
862  {
863  if ((is_complete || why == ForInfo) && buff != 0)
864  {
865  int res2 = m_oss->Lfn2Pfn(f_name.c_str(), buff, blen);
866  if (res2 < 0)
867  return res2;
868 
869  // Normally, files are owned by us but when direct cache access
870  // is wanted and possible, make sure the file is world readable.
871  if (why == ForAccess)
872  {mode_t mode = (forall ? worldReadable : groupReadable);
873  if (((sbuff.st_mode & worldReadable) != mode)
874  && (m_oss->Chmod(f_name.c_str(), mode) != XrdOssOK))
875  {is_complete = false;
876  *buff = 0;
877  }
878  }
879  }
880 
881  TRACE(Info, "LocalFilePath '" << curl << "', why=" << lfpReason[why] <<
882  (is_complete ? " -> FILE_COMPLETE_IN_CACHE" : " -> EREMOTE"));
883 
884  return is_complete ? 0 : -EREMOTE;
885  }
886  }
887  }
888 
889  TRACE(Info, "LocalFilePath '" << curl << "', why=" << lfpReason[why] << " -> ENOENT");
890  return -ENOENT;
891 }
892 
893 //______________________________________________________________________________
894 // If supported, write file_size as xattr to cinfo file.
895 //------------------------------------------------------------------------------
896 void Cache::WriteFileSizeXAttr(int cinfo_fd, long long file_size)
897 {
898  if (m_metaXattr) {
899  int res = XrdSysXAttrActive->Set("pfc.fsize", &file_size, sizeof(long long), 0, cinfo_fd, 0);
900  if (res != 0) {
901  TRACE(Debug, "WriteFileSizeXAttr error setting xattr " << res);
902  }
903  }
904 }
905 
906 //______________________________________________________________________________
907 // Determine full size of the data file from the corresponding cinfo-file name.
908 // Attempts to read xattr first and falls back to reading of the cinfo file.
909 // Returns -error on failure.
910 //------------------------------------------------------------------------------
911 long long Cache::DetermineFullFileSize(const std::string &cinfo_fname)
912 {
913  if (m_metaXattr) {
914  char pfn[4096];
915  m_oss->Lfn2Pfn(cinfo_fname.c_str(), pfn, 4096);
916  long long fsize = -1ll;
917  int res = XrdSysXAttrActive->Get("pfc.fsize", &fsize, sizeof(long long), pfn);
918  if (res == sizeof(long long))
919  {
920  return fsize;
921  }
922  else
923  {
924  TRACE(Debug, "DetermineFullFileSize error getting xattr " << res);
925  }
926  }
927 
928  XrdOssDF *infoFile = m_oss->newFile(m_configuration.m_username.c_str());
929  XrdOucEnv env;
930  long long ret;
931  int res = infoFile->Open(cinfo_fname.c_str(), O_RDONLY, 0600, env);
932  if (res < 0) {
933  ret = res;
934  } else {
935  Info info(m_trace, 0);
936  if ( ! info.Read(infoFile, cinfo_fname.c_str())) {
937  ret = -EBADF;
938  } else {
939  ret = info.GetFileSize();
940  }
941  infoFile->Close();
942  }
943  delete infoFile;
944  return ret;
945 }
946 
947 //______________________________________________________________________________
948 // Calculate if the file is to be considered cached for the purposes of
949 // only-if-cached and setting of atime of the Stat() calls.
950 // Returns true if the file is to be conidered cached.
951 //------------------------------------------------------------------------------
952 bool Cache::DecideIfConsideredCached(long long file_size, long long bytes_on_disk)
953 {
954  if (file_size == 0 || bytes_on_disk >= file_size)
955  return true;
956 
957  double frac_on_disk = (double) bytes_on_disk / file_size;
958 
959  if (file_size <= m_configuration.m_onlyIfCachedMinSize)
960  {
961  if (frac_on_disk >= m_configuration.m_onlyIfCachedMinFrac)
962  return true;
963  }
964  else
965  {
966  if (bytes_on_disk >= m_configuration.m_onlyIfCachedMinSize &&
967  frac_on_disk >= m_configuration.m_onlyIfCachedMinFrac)
968  return true;
969  }
970  return false;
971 }
972 
973 //______________________________________________________________________________
974 // Check if the file is cached including m_onlyIfCachedMinSize and m_onlyIfCachedMinFrac
975 // pfc configuration parameters. The logic of accessing the Info file is the same
976 // as in Cache::LocalFilePath.
984 //------------------------------------------------------------------------------
985 int Cache::ConsiderCached(const char *curl)
986 {
987  static const char* tpfx = "ConsiderCached ";
988 
989  TRACE(Debug, tpfx << curl);
990 
991  XrdCl::URL url(curl);
992  std::string f_name = url.GetPath();
993 
994  File *file = nullptr;
995  {
996  XrdSysCondVarHelper lock(&m_active_cond);
997  auto it = m_active.find(f_name);
998  if (it != m_active.end()) {
999  file = it->second;
1000  inc_ref_cnt(file, false, false);
1001  }
1002  }
1003  if (file) {
1004  struct stat sbuff;
1005  int res = file->Fstat(sbuff);
1006  dec_ref_cnt(file, false);
1007  if (res)
1008  return res;
1009  // DecideIfConsideredCached() already called in File::Fstat().
1010  return sbuff.st_atime > 0 ? 0 : -EREMOTE;
1011  }
1012 
1013  struct stat sbuff;
1014  int res = m_oss->Stat(f_name.c_str(), &sbuff);
1015  if (res != XrdOssOK) {
1016  TRACE(Debug, tpfx << curl << " -> " << res);
1017  return res;
1018  }
1019  if (S_ISDIR(sbuff.st_mode))
1020  {
1021  TRACE(Debug, tpfx << curl << " -> EISDIR");
1022  return -EISDIR;
1023  }
1024 
1025  long long file_size = DetermineFullFileSize(f_name + Info::s_infoExtension);
1026  if (file_size < 0) {
1027  TRACE(Debug, tpfx << curl << " -> " << file_size);
1028  return (int) file_size;
1029  }
1030  bool is_cached = DecideIfConsideredCached(file_size, sbuff.st_blocks * 512ll);
1031 
1032  return is_cached ? 0 : -EREMOTE;
1033 }
1034 
1035 //______________________________________________________________________________
1043 //------------------------------------------------------------------------------
1044 
1045 int Cache::Prepare(const char *curl, int oflags, mode_t mode)
1046 {
1047  XrdCl::URL url(curl);
1048  std::string f_name = url.GetPath();
1049  std::string i_name = f_name + Info::s_infoExtension;
1050 
1051  // Do not allow write access.
1052  if (oflags & (O_WRONLY | O_RDWR | O_APPEND | O_CREAT))
1053  {
1054  TRACE(Warning, "Prepare write access requested on file " << f_name << ". Denying access.");
1055  return -EROFS;
1056  }
1057 
1058  // Intercept xrdpfc_command requests.
1059  if (m_configuration.m_allow_xrdpfc_command && strncmp("/xrdpfc_command/", f_name.c_str(), 16) == 0)
1060  {
1061  // Schedule a job to process command request.
1062  {
1063  CommandExecutor *ce = new CommandExecutor(f_name, "CommandExecutor");
1064 
1065  schedP->Schedule(ce);
1066  }
1067 
1068  return -EAGAIN;
1069  }
1070 
1071  {
1072  XrdSysCondVarHelper lock(&m_active_cond);
1073  m_purge_delay_set.insert(f_name);
1074  }
1075 
1076  struct stat sbuff;
1077  if (m_oss->Stat(i_name.c_str(), &sbuff) == XrdOssOK)
1078  {
1079  TRACE(Dump, "Prepare defer open " << f_name);
1080  return 1;
1081  }
1082  else
1083  {
1084  return 0;
1085  }
1086 }
1087 
1088 //______________________________________________________________________________
1089 // virtual method of XrdOucCache.
1094 //------------------------------------------------------------------------------
1095 
1096 int Cache::Stat(const char *curl, struct stat &sbuff)
1097 {
1098  const char *tpfx = "Stat ";
1099 
1100  XrdCl::URL url(curl);
1101  std::string f_name = url.GetPath();
1102 
1103  File *file = nullptr;
1104  {
1105  XrdSysCondVarHelper lock(&m_active_cond);
1106  auto it = m_active.find(f_name);
1107  if (it != m_active.end()) {
1108  file = it->second;
1109  inc_ref_cnt(file, false, false);
1110  }
1111  }
1112  if (file) {
1113  int res = file->Fstat(sbuff);
1114  dec_ref_cnt(file, false);
1115  TRACE(Debug, tpfx << "from active file " << curl << " -> " << res);
1116  return res;
1117  }
1118 
1119  int res = m_oss->Stat(f_name.c_str(), &sbuff);
1120  if (res != XrdOssOK) {
1121  TRACE(Debug, tpfx << curl << " -> " << res);
1122  return 1; // res; -- for only-if-cached
1123  }
1124  if (S_ISDIR(sbuff.st_mode))
1125  {
1126  TRACE(Debug, tpfx << curl << " -> EISDIR");
1127  return -EISDIR;
1128  }
1129 
1130  long long file_size = DetermineFullFileSize(f_name + Info::s_infoExtension);
1131  if (file_size < 0) {
1132  TRACE(Debug, tpfx << curl << " -> " << file_size);
1133  return 1; // (int) file_size; -- for only-if-cached
1134  }
1135  sbuff.st_size = file_size;
1136  bool is_cached = DecideIfConsideredCached(file_size, sbuff.st_blocks * 512ll);
1137  if ( ! is_cached) {
1138  sbuff.st_ctime = sbuff.st_mtime = sbuff.st_atime = 0;
1139  }
1140 
1141  TRACE(Debug, tpfx << "from disk " << curl << " -> " << res);
1142 
1143  return 0;
1144 }
1145 
1146 //______________________________________________________________________________
1147 // virtual method of XrdOucCache.
1151 //------------------------------------------------------------------------------
1152 
1153 int Cache::Unlink(const char *curl)
1154 {
1155  XrdCl::URL url(curl);
1156  std::string f_name = url.GetPath();
1157 
1158  // printf("Unlink url=%s\n\t fname=%s\n", curl, f_name.c_str());
1159 
1160  return UnlinkFile(f_name, false);
1161 }
1162 
1163 int Cache::UnlinkFile(const std::string& f_name, bool fail_if_open)
1164 {
1165  static const char* trc_pfx = "UnlinkFile ";
1166  ActiveMap_i it;
1167  File *file = 0;
1168  {
1169  XrdSysCondVarHelper lock(&m_active_cond);
1170 
1171  it = m_active.find(f_name);
1172 
1173  if (it != m_active.end())
1174  {
1175  if (fail_if_open)
1176  {
1177  TRACE(Info, trc_pfx << f_name << ", file currently open and force not requested - denying request");
1178  return -EBUSY;
1179  }
1180 
1181  // Null File* in m_active map means an operation is ongoing, probably
1182  // Attach() with possible File::Open(). Ask for retry.
1183  if (it->second == 0)
1184  {
1185  TRACE(Info, trc_pfx << f_name << ", an operation on this file is ongoing - denying request");
1186  return -EAGAIN;
1187  }
1188 
1189  file = it->second;
1191  it->second = 0;
1192  }
1193  else
1194  {
1195  it = m_active.insert(std::make_pair(f_name, (File*) 0)).first;
1196  }
1197  }
1198 
1199  if (file)
1200  {
1201  RemoveWriteQEntriesFor(file);
1202  }
1203 
1204  std::string i_name = f_name + Info::s_infoExtension;
1205 
1206  // Unlink file & cinfo
1207  struct stat f_stat;
1208  bool stat_ok = (m_oss->Stat(f_name.c_str(), &f_stat) == XrdOssOK);
1209  int f_ret = m_oss->Unlink(f_name.c_str());
1210  int i_ret = m_oss->Unlink(i_name.c_str());
1211 
1212  if (stat_ok)
1213  m_res_mon->register_file_purge(f_name, f_stat.st_blocks);
1214 
1215  TRACE(Debug, trc_pfx << f_name << ", f_ret=" << f_ret << ", i_ret=" << i_ret);
1216 
1217  {
1218  XrdSysCondVarHelper lock(&m_active_cond);
1219 
1220  m_active.erase(it);
1221  }
1222 
1223  return std::min(f_ret, i_ret);
1224 }
int DoIt(int argpnt, int argc, char **argv, bool singleshot)
Definition: XrdAccTest.cc:262
#define TRACE_Debug
Definition: XrdCmsTrace.hh:37
@ Warning
#define XrdOssOK
Definition: XrdOss.hh:50
std::string obfuscateAuth(const std::string &input)
#define TRACE_Dump
Definition: XrdPfcTrace.hh:11
#define TRACE_PC(act, pre_code, x)
Definition: XrdPfcTrace.hh:59
#define TRACE_INT(act, x)
Definition: XrdPfcTrace.hh:52
void * ResourceMonitorThread(void *)
Definition: XrdPfc.cc:55
XrdOucCache * XrdOucGetCache(XrdSysLogger *logger, const char *config_filename, const char *parameters, XrdOucEnv *env)
Definition: XrdPfc.cc:77
void * PrefetchThread(void *)
Definition: XrdPfc.cc:67
XrdSysXAttr * XrdSysXAttrActive
Definition: XrdSysFAttr.cc:61
void * ProcessWriteTaskThread(void *)
Definition: XrdPfc.cc:61
int stat(const char *path, struct stat *buf)
#define TRACE(act, x)
Definition: XrdTrace.hh:63
URL representation.
Definition: XrdClURL.hh:31
const std::string & GetPath() const
Get the path.
Definition: XrdClURL.hh:217
Definition: XrdJob.hh:43
virtual int Close(long long *retsz=0)=0
virtual int Open(const char *path, int Oflag, mode_t Mode, XrdOucEnv &env)
Definition: XrdOss.hh:200
virtual int Chmod(const char *path, mode_t mode, XrdOucEnv *envP=0)=0
virtual int Lfn2Pfn(const char *Path, char *buff, int blen)
Definition: XrdOss.hh:873
virtual XrdOssDF * newFile(const char *tident)=0
virtual int Stat(const char *path, struct stat *buff, int opts=0, XrdOucEnv *envP=0)=0
virtual int Unlink(const char *path, int Opts=0, XrdOucEnv *envP=0)=0
virtual const char * Path()=0
virtual int Fstat(struct stat &sbuff)
Definition: XrdOucCache.hh:148
virtual const char * Location(bool refresh=false)
Definition: XrdOucCache.hh:161
void * GetPtr(const char *varname)
Definition: XrdOucEnv.cc:281
void PutPtr(const char *varname, void *value)
Definition: XrdOucEnv.cc:316
int get_size() const
Definition: XrdPfcFile.hh:142
File * get_file() const
Definition: XrdPfcFile.hh:146
long long m_offset
Definition: XrdPfcFile.hh:120
Attaches/creates and detaches/deletes cache-io objects for disk based cache.
Definition: XrdPfc.hh:151
long long DetermineFullFileSize(const std::string &cinfo_fname)
Definition: XrdPfc.cc:911
void FileSyncDone(File *, bool high_debug)
Definition: XrdPfc.cc:543
File * GetFile(const std::string &, IO *, long long off=0, long long filesize=0)
Definition: XrdPfc.cc:390
static const Configuration & Conf()
Definition: XrdPfc.cc:135
bool Config(const char *config_filename, const char *parameters)
Parse configuration file.
virtual int LocalFilePath(const char *url, char *buff=0, int blen=0, LFP_Reason why=ForAccess, bool forall=false)
Definition: XrdPfc.cc:779
virtual int Stat(const char *url, struct stat &sbuff)
Definition: XrdPfc.cc:1096
const Configuration & RefConfiguration() const
Reference XrdPfc configuration.
Definition: XrdPfc.hh:203
static ResourceMonitor & ResMon()
Definition: XrdPfc.cc:136
bool IsFileActiveOrPurgeProtected(const std::string &) const
Definition: XrdPfc.cc:662
void ClearPurgeProtectedSet()
Definition: XrdPfc.cc:670
void ReleaseRAM(char *buf, long long size)
Definition: XrdPfc.cc:372
virtual int ConsiderCached(const char *url)
Definition: XrdPfc.cc:985
static Cache & GetInstance()
Singleton access.
Definition: XrdPfc.cc:133
void DeRegisterPrefetchFile(File *)
Definition: XrdPfc.cc:696
void ExecuteCommandUrl(const std::string &command_url)
void RegisterPrefetchFile(File *)
Definition: XrdPfc.cc:680
void WriteFileSizeXAttr(int cinfo_fd, long long file_size)
Definition: XrdPfc.cc:896
void Prefetch()
Definition: XrdPfc.cc:737
void ReleaseFile(File *, IO *)
Definition: XrdPfc.cc:472
void AddWriteTask(Block *b, bool from_read)
Add downloaded block in write queue.
Definition: XrdPfc.cc:222
Cache(XrdSysLogger *logger, XrdOucEnv *env)
Constructor.
Definition: XrdPfc.cc:159
bool Decide(XrdOucCacheIO *)
Makes decision if the original XrdOucCacheIO should be cached.
Definition: XrdPfc.cc:138
int UnlinkFile(const std::string &f_name, bool fail_if_open)
Remove cinfo and data files from cache.
Definition: XrdPfc.cc:1163
virtual XrdOucCacheIO * Attach(XrdOucCacheIO *ioP, int opts=0)=0
Obtain a new IO object that fronts existing XrdOucCacheIO.
static XrdScheduler * schedP
Definition: XrdPfc.hh:289
File * GetNextFileToPrefetch()
Definition: XrdPfc.cc:718
ResourceMonitor & RefResMon()
Definition: XrdPfc.hh:284
long long WritesSinceLastCall()
Definition: XrdPfc.cc:321
void ProcessWriteTasks()
Separate task which writes blocks from ram to disk.
Definition: XrdPfc.cc:274
virtual int Unlink(const char *url)
Definition: XrdPfc.cc:1153
void RemoveWriteQEntriesFor(File *f)
Remove blocks from write queue which belong to given prefetch. This method is used at the time of Fil...
Definition: XrdPfc.cc:241
static const Cache & TheOne()
Definition: XrdPfc.cc:134
char * RequestRAM(long long size)
Definition: XrdPfc.cc:332
virtual int Prepare(const char *url, int oflags, mode_t mode)
Definition: XrdPfc.cc:1045
bool DecideIfConsideredCached(long long file_size, long long bytes_on_disk)
Definition: XrdPfc.cc:952
static Cache & CreateInstance(XrdSysLogger *logger, XrdOucEnv *env)
Singleton creation.
Definition: XrdPfc.cc:126
Base class for selecting which files should be cached.
virtual bool Decide(const std::string &, XrdOss &) const =0
bool FinalizeSyncBeforeExit()
Returns true if any of blocks need sync. Called from Cache::dec_ref_cnt on zero ref cnt.
Definition: XrdPfcFile.cc:287
const char * lPath() const
Log path.
Definition: XrdPfcFile.cc:1501
void WriteBlockToDisk(Block *b)
Definition: XrdPfcFile.cc:1032
static File * FileOpen(const std::string &path, long long offset, long long fileSize)
Static constructor that also does Open. Returns null ptr if Open fails.
Definition: XrdPfcFile.cc:109
int GetNBlocks() const
Definition: XrdPfcFile.hh:286
void Prefetch()
Definition: XrdPfcFile.cc:1516
std::string GetRemoteLocations() const
Definition: XrdPfcFile.cc:1613
const Info::AStat * GetLastAccessStats() const
Definition: XrdPfcFile.hh:283
size_t GetAccessCnt() const
Definition: XrdPfcFile.hh:284
int Fstat(struct stat &sbuff)
Definition: XrdPfcFile.cc:527
void AddIO(IO *io)
Definition: XrdPfcFile.cc:311
long long GetPrefetchedBytes() const
Definition: XrdPfcFile.hh:288
int GetBlockSize() const
Definition: XrdPfcFile.hh:285
int GetNDownloadedBlocks() const
Definition: XrdPfcFile.hh:287
void BlocksRemovedFromWriteQ(std::list< Block * > &)
Handle removal of a set of blocks from Cache's write queue.
Definition: XrdPfcFile.cc:181
void initiate_emergency_shutdown()
Definition: XrdPfcFile.cc:122
int inc_ref_cnt()
Definition: XrdPfcFile.hh:295
void Sync()
Sync file cache inf o and output data with disk.
Definition: XrdPfcFile.cc:1115
int dec_ref_cnt()
Definition: XrdPfcFile.hh:296
int get_ref_cnt()
Definition: XrdPfcFile.hh:294
long long GetFileSize() const
Definition: XrdPfcFile.hh:275
const Stats & RefStats() const
Definition: XrdPfcFile.hh:289
void RemoveIO(IO *io)
Definition: XrdPfcFile.cc:348
const std::string & GetLocalPath() const
Definition: XrdPfcFile.hh:270
bool is_in_emergency_shutdown()
Definition: XrdPfcFile.hh:299
Downloads original file into multiple files, chunked into blocks. Only blocks that are asked for are ...
Downloads original file into a single file on local disk. Handles read requests as they come along.
Definition: XrdPfcIOFile.hh:39
bool HasFile() const
Check if File was opened successfully.
Definition: XrdPfcIOFile.hh:48
Base cache-io class that implements some XrdOucCacheIO abstract methods.
Definition: XrdPfcIO.hh:16
Status of cached file. Can be read from and written into a binary file.
Definition: XrdPfcInfo.hh:41
static const char * s_infoExtension
Definition: XrdPfcInfo.hh:309
void WriteIOStatSingle(long long bytes_disk)
Write single open/close time for given bytes read from disk.
Definition: XrdPfcInfo.cc:446
bool Write(XrdOssDF *fp, const char *dname, const char *fname=0)
Definition: XrdPfcInfo.cc:268
bool IsComplete() const
Get complete status.
Definition: XrdPfcInfo.hh:447
long long GetFileSize() const
Get file size.
Definition: XrdPfcInfo.hh:442
bool Read(XrdOssDF *fp, const char *dname, const char *fname=0)
Read content of cinfo file into this object.
Definition: XrdPfcInfo.cc:296
void register_file_purge(DirState *target, long long size_in_st_blocks)
Statistics of cache utilisation by a File object.
Definition: XrdPfcStats.hh:35
int m_NCksumErrors
number of checksum errors while getting data from remote
Definition: XrdPfcStats.hh:44
long long m_BytesWritten
number of bytes written to disk
Definition: XrdPfcStats.hh:42
void Schedule(XrdJob *jp)
void Say(const char *text1, const char *text2=0, const char *txt3=0, const char *text4=0, const char *text5=0, const char *txt6=0)
Definition: XrdSysError.cc:141
static int Run(pthread_t *, void *(*proc)(void *), void *arg, int opts=0, const char *desc=0)
static void Wait(int milliseconds)
Definition: XrdSysTimer.cc:227
virtual int Get(const char *Aname, void *Aval, int Avsz, const char *Path, int fd=-1)=0
virtual int Set(const char *Aname, const void *Aval, int Avsz, const char *Path, int fd=-1, int isNew=0)=0
bool Insert(const char *data, int dlen)
Definition: XrdPfc.hh:41
Contains parameters configurable from the xrootd config file.
Definition: XrdPfc.hh:64
long long m_RamAbsAvailable
available from configuration
Definition: XrdPfc.hh:108
bool m_allow_xrdpfc_command
flag for enabling access to /xrdpfc-command/ functionality.
Definition: XrdPfc.hh:85
int m_prefetch_max_blocks
maximum number of blocks to prefetch per file
Definition: XrdPfc.hh:112
int m_RamKeepStdBlocks
number of standard-sized blocks kept after release
Definition: XrdPfc.hh:109
long long m_bufferSize
prefetch buffer size, default 1MB
Definition: XrdPfc.hh:107
int m_wqueue_blocks
maximum number of blocks written per write-queue loop
Definition: XrdPfc.hh:110
std::string m_username
username passed to oss plugin
Definition: XrdPfc.hh:87
double m_onlyIfCachedMinFrac
minimum fraction of downloaded file, used by only-if-cached CGI option
Definition: XrdPfc.hh:122
long long m_onlyIfCachedMinSize
minumum size of downloaded file, used by only-if-cached CGI option
Definition: XrdPfc.hh:121
Access statistics.
Definition: XrdPfcInfo.hh:57
long long BytesHit
read from cache
Definition: XrdPfcInfo.hh:64
long long BytesBypassed
read from remote and dropped
Definition: XrdPfcInfo.hh:66
time_t DetachTime
close time
Definition: XrdPfcInfo.hh:59
long long BytesMissed
read from remote and cached
Definition: XrdPfcInfo.hh:65
time_t AttachTime
open time
Definition: XrdPfcInfo.hh:58