XRootD
Loading...
Searching...
No Matches
XrdPfcFile.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
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
20#include "XrdPfcFile.hh"
21#include "XrdPfc.hh"
23#include "XrdPfcIO.hh"
24#include "XrdPfcTrace.hh"
25
27#include "XrdSys/XrdSysTimer.hh"
28#include "XrdOss/XrdOss.hh"
29#include "XrdOuc/XrdOucEnv.hh"
30#include "XrdOuc/XrdOucJson.hh"
32
33#include "XrdCl/XrdClURL.hh"
35
36#include <cassert>
37#include <cstdio>
38#include <sstream>
39#include <unordered_map>
40
41#include <fcntl.h>
42
43using namespace XrdPfc;
44
45namespace
46{
47
48const int BLOCK_WRITE_MAX_ATTEMPTS = 4;
49
50Cache* cache() { return &Cache::GetInstance(); }
51
52}
53
54const char *File::m_traceID = "File";
55
56//------------------------------------------------------------------------------
57
58File::File(const std::string& path, long long iOffset, long long iFileSize) :
59 m_ref_cnt(0),
60 m_data_file(0),
61 m_info_file(0),
62 m_cfi(Cache::TheOne().GetTrace(), Cache::TheOne().is_prefetch_enabled()),
63 m_filename(path),
64 m_offset(iOffset),
65 m_file_size(iFileSize),
66 m_current_io(m_io_set.end()),
67 m_ios_in_detach(0),
68 m_non_flushed_cnt(0),
69 m_in_sync(false),
70 m_detach_time_logged(false),
71 m_in_shutdown(false),
72 m_state_cond(0),
73 m_block_size(0),
74 m_num_blocks(0),
75 m_resmon_token(-1),
76 m_prefetch_state(kOff),
77 m_prefetch_bytes(0),
78 m_prefetch_read_cnt(0),
79 m_prefetch_hit_cnt(0),
80 m_prefetch_score(0)
81{}
82
83File::~File()
84{
85 TRACEF(Debug, "~File() for ");
86}
87
88void File::Close()
89{
90 // Close is called while nullptr is put into Cache::m_active map, see Cache::dec_ref_count(File*).
91 // A stat is called after close to re-check that m_stat_blocks have been reported correctly
92 // to the resource-monitor. Note that the reporting is already clamped down to m_file_size
93 // in report_and_merge_delta_stats() below.
94 //
95 // XFS can pre-allocate significant amount of blocks (1 GB at 1GB mark, 4 GB above 4GB) and those
96 // get reported in as stat.st_blocks.
97 // The reported number is correct in a stat immediately following a close.
98 // If one starts off by writing the last byte of the file, this pre-allocation does not get
99 // triggered up to that point. But comes back with a vengeance right after.
100 //
101 // To be determined if other FSes do something similar (Ceph, ZFS, ...). Ext4 doesn't.
102
103 if (m_info_file)
104 {
105 TRACEF(Debug, "Close() closing info-file ");
106 m_info_file->Close();
107 delete m_info_file;
108 m_info_file = nullptr;
109 }
110
111 if (m_data_file)
112 {
113 TRACEF(Debug, "Close() closing data-file ");
114 m_data_file->Close();
115 delete m_data_file;
116 m_data_file = nullptr;
117 }
118
119 if (m_resmon_token >= 0)
120 {
121 // Last update of file stats has been sent from the final Sync unless we are in_shutdown --
122 // but in this case the file will get unlinked by the cache and reported as purge event.
123 // We check if the reported st_blocks so far is correct.
124 if (m_stats.m_BytesWritten > 0 && ! m_in_shutdown) {
125 struct stat s;
126 int sr = Cache::GetInstance().GetOss()->Stat(m_filename.c_str(), &s);
127 if (sr == 0 && s.st_blocks != m_st_blocks) {
128 Stats stats;
129 stats.m_StBlocksAdded = s.st_blocks - m_st_blocks;
130 m_st_blocks = s.st_blocks;
131 Cache::ResMon().register_file_update_stats(m_resmon_token, stats);
132 }
133 }
134
135 Cache::ResMon().register_file_close(m_resmon_token, time(0), m_stats);
136 }
137
138 TRACEF(Debug, "Close() finished, prefetch score = " << m_prefetch_score);
139}
140
141//------------------------------------------------------------------------------
142
143File* File::FileOpen(const std::string &path, long long offset, long long fileSize, XrdOucCacheIO *inputIO)
144{
145 File *file = new File(path, offset, fileSize);
146 if ( ! file->Open(inputIO))
147 {
148 delete file;
149 file = 0;
150 }
151 return file;
152}
153
154//------------------------------------------------------------------------------
155
157{
158 // Called from Cache::Unlink() when the file is currently open.
159 // Cache::Unlink is also called on FSync error and when wrong number of bytes
160 // is received from a remote read.
161 //
162 // From this point onward the file will not be written to, cinfo file will
163 // not be updated, and all new read requests will return -ENOENT.
164 //
165 // File's entry in the Cache's active map is set to nullptr and will be
166 // removed from there shortly, in any case, well before this File object
167 // shuts down. Cache::Unlink() also reports the appropriate purge event.
168
169 XrdSysCondVarHelper _lck(m_state_cond);
170
171 m_in_shutdown = true;
172
173 if (m_prefetch_state != kStopped && m_prefetch_state != kComplete)
174 {
175 m_prefetch_state = kStopped;
176 cache()->DeRegisterPrefetchFile(this);
177 }
178
179 report_and_merge_delta_stats();
180
181 return m_st_blocks;
182}
183
184//------------------------------------------------------------------------------
185
186void File::check_delta_stats()
187{
188 // Called under m_state_cond lock.
189 // BytesWritten indirectly trigger an unconditional merge through periodic Sync().
190 if (m_delta_stats.BytesReadAndWritten() >= m_resmon_report_threshold && ! m_in_shutdown)
191 report_and_merge_delta_stats();
192}
193
194void File::report_and_merge_delta_stats()
195{
196 // Called under m_state_cond lock.
197 struct stat s;
198 m_data_file->Fstat(&s);
199 // Do not report st_blocks beyond 4kB round-up over m_file_size. Some FSs report
200 // aggressive pre-allocation in this field (XFS, 4GB).
201 long long max_st_blocks_to_report = (m_file_size & 0xfff) ? ((m_file_size >> 12) + 1) << 3
202 : m_file_size >> 9;
203 long long st_blocks_to_report = std::min((long long) s.st_blocks, max_st_blocks_to_report);
204 m_delta_stats.m_StBlocksAdded = st_blocks_to_report - m_st_blocks;
205 m_st_blocks = st_blocks_to_report;
206 Cache::ResMon().register_file_update_stats(m_resmon_token, m_delta_stats);
207 m_stats.AddUp(m_delta_stats);
208 m_delta_stats.Reset();
209}
210
211//------------------------------------------------------------------------------
212
214{
215 TRACEF(Dump, "BlockRemovedFromWriteQ() block = " << (void*) b << " idx= " << b->m_offset/m_block_size);
216
217 XrdSysCondVarHelper _lck(m_state_cond);
218 dec_ref_count(b);
219}
220
221void File::BlocksRemovedFromWriteQ(std::list<Block*>& blocks)
222{
223 TRACEF(Dump, "BlocksRemovedFromWriteQ() n_blocks = " << blocks.size());
224
225 XrdSysCondVarHelper _lck(m_state_cond);
226
227 for (std::list<Block*>::iterator i = blocks.begin(); i != blocks.end(); ++i)
228 {
229 dec_ref_count(*i);
230 }
231}
232
233//------------------------------------------------------------------------------
234
236{
237 std::string loc(io->GetLocation());
238 XrdSysCondVarHelper _lck(m_state_cond);
239 insert_remote_location(loc);
240}
241
242//------------------------------------------------------------------------------
243
245{
246 // Returns true if delay is needed.
247
248 TRACEF(Debug, "ioActive start for io " << io);
249
250 std::string loc(io->GetLocation());
251
252 {
253 XrdSysCondVarHelper _lck(m_state_cond);
254
255 IoSet_i mi = m_io_set.find(io);
256
257 if (mi != m_io_set.end())
258 {
259 unsigned int n_active_reads = io->m_active_read_reqs;
260
261 TRACE(Info, "ioActive for io " << io <<
262 ", active_reads " << n_active_reads <<
263 ", active_prefetches " << io->m_active_prefetches <<
264 ", allow_prefetching " << io->m_allow_prefetching <<
265 ", ios_in_detach " << m_ios_in_detach);
266 TRACEF(Info,
267 "\tio_map.size() " << m_io_set.size() <<
268 ", block_map.size() " << m_block_map.size() << ", file");
269
270 insert_remote_location(loc);
271
272 io->m_allow_prefetching = false;
273 io->m_in_detach = true;
274
275 // Check if any IO is still available for prfetching. If not, stop it.
276 if (m_prefetch_state == kOn || m_prefetch_state == kHold)
277 {
278 if ( ! select_current_io_or_disable_prefetching(false) )
279 {
280 TRACEF(Debug, "ioActive stopping prefetching after io " << io << " retreat.");
281 }
282 }
283
284 // On last IO, consider write queue blocks. Note, this also contains
285 // blocks being prefetched.
286
287 bool io_active_result;
288
289 if (n_active_reads > 0)
290 {
291 io_active_result = true;
292 }
293 else if (m_io_set.size() - m_ios_in_detach == 1)
294 {
295 io_active_result = ! m_block_map.empty();
296 }
297 else
298 {
299 io_active_result = io->m_active_prefetches > 0;
300 }
301
302 if ( ! io_active_result)
303 {
304 ++m_ios_in_detach;
305 }
306
307 TRACEF(Info, "ioActive for io " << io << " returning " << io_active_result << ", file");
308
309 return io_active_result;
310 }
311 else
312 {
313 TRACEF(Error, "ioActive io " << io <<" not found in IoSet. This should not happen.");
314 return false;
315 }
316 }
317}
318
319//------------------------------------------------------------------------------
320
322{
323 XrdSysCondVarHelper _lck(m_state_cond);
324 m_detach_time_logged = false;
325}
326
328{
329 // Returns true if sync is required.
330 // This method is called after corresponding IO is detached from PosixCache.
331
332 XrdSysCondVarHelper _lck(m_state_cond);
333 if ( ! m_in_shutdown)
334 {
335 if ( ! m_writes_during_sync.empty() || m_non_flushed_cnt > 0 || ! m_detach_time_logged)
336 {
337 report_and_merge_delta_stats();
338 m_cfi.WriteIOStatDetach(m_stats);
339 m_detach_time_logged = true;
340 m_in_sync = true;
341 TRACEF(Debug, "FinalizeSyncBeforeExit requesting sync to write detach stats");
342 return true;
343 }
344 }
345 TRACEF(Debug, "FinalizeSyncBeforeExit sync not required");
346 return false;
347}
348
349//------------------------------------------------------------------------------
350
352{
353 // Called from Cache::GetFile() when a new IO asks for the file.
354
355 TRACEF(Debug, "AddIO() io = " << (void*)io);
356
357 time_t now = time(0);
358 std::string loc(io->GetLocation());
359
360 m_state_cond.Lock();
361
362 IoSet_i mi = m_io_set.find(io);
363
364 if (mi == m_io_set.end())
365 {
366 m_io_set.insert(io);
367 io->m_attach_time = now;
368 m_delta_stats.IoAttach();
369
370 insert_remote_location(loc);
371
372 if (m_prefetch_state == kStopped)
373 {
374 m_prefetch_state = kOn;
375 cache()->RegisterPrefetchFile(this);
376 }
377 }
378 else
379 {
380 TRACEF(Error, "AddIO() io = " << (void*)io << " already registered.");
381 }
382
383 m_state_cond.UnLock();
384}
385
386//------------------------------------------------------------------------------
387
389{
390 // Called from Cache::ReleaseFile.
391
392 TRACEF(Debug, "RemoveIO() io = " << (void*)io);
393
394 time_t now = time(0);
395
396 m_state_cond.Lock();
397
398 IoSet_i mi = m_io_set.find(io);
399
400 if (mi != m_io_set.end())
401 {
402 if (mi == m_current_io)
403 {
404 ++m_current_io;
405 }
406
407 m_delta_stats.IoDetach(now - io->m_attach_time);
408 m_io_set.erase(mi);
409 --m_ios_in_detach;
410
411 if (m_io_set.empty() && m_prefetch_state != kStopped && m_prefetch_state != kComplete)
412 {
413 TRACEF(Error, "RemoveIO() io = " << (void*)io << " Prefetching is not stopped/complete -- it should be by now.");
414 m_prefetch_state = kStopped;
415 cache()->DeRegisterPrefetchFile(this);
416 }
417 }
418 else
419 {
420 TRACEF(Error, "RemoveIO() io = " << (void*)io << " is NOT registered.");
421 }
422
423 m_state_cond.UnLock();
424}
425
426//------------------------------------------------------------------------------
427
428bool File::Open(XrdOucCacheIO *inputIO)
429{
430 // Sets errno accordingly.
431
432 static const char *tpfx = "Open() ";
433
434 TRACEF(Dump, tpfx << "entered");
435
436 // Before touching anything, check with ResourceMonitor if a scan is in progress.
437 // This function will wait internally if needed until it is safe to proceed.
438 Cache::ResMon().CrossCheckIfScanIsInProgress(m_filename, m_state_cond);
439
441
442 XrdOss &myOss = * Cache::GetInstance().GetOss();
443 const char *myUser = conf.m_username.c_str();
444 XrdOucEnv myEnv;
445 struct stat data_stat, info_stat;
446
447 std::string ifn = m_filename + Info::s_infoExtension;
448
449 bool data_existed = (myOss.Stat(m_filename.c_str(), &data_stat) == XrdOssOK);
450 bool info_existed = (myOss.Stat(ifn.c_str(), &info_stat) == XrdOssOK);
451
452 // Create the data file itself.
453 char size_str[32]; sprintf(size_str, "%lld", m_file_size);
454 myEnv.Put("oss.asize", size_str);
455 myEnv.Put("oss.cgroup", conf.m_data_space.c_str());
456
457 int res;
458
459 if ((res = myOss.Create(myUser, m_filename.c_str(), 0600, myEnv, XRDOSS_mkpath)) != XrdOssOK)
460 {
461 TRACEF(Error, tpfx << "Create failed " << ERRNO_AND_ERRSTR(-res));
462 errno = -res;
463 return false;
464 }
465
466 m_data_file = myOss.newFile(myUser);
467 if ((res = m_data_file->Open(m_filename.c_str(), O_RDWR, 0600, myEnv)) != XrdOssOK)
468 {
469 TRACEF(Error, tpfx << "Open failed " << ERRNO_AND_ERRSTR(-res));
470 errno = -res;
471 delete m_data_file; m_data_file = 0;
472 if ( ! data_existed) myOss.Unlink(m_filename.c_str());
473 return false;
474 }
475
476 myEnv.Put("oss.asize", "64k"); // Advisory, block-map and access list lengths vary.
477 myEnv.Put("oss.cgroup", conf.m_meta_space.c_str());
478 if ((res = myOss.Create(myUser, ifn.c_str(), 0600, myEnv, XRDOSS_mkpath)) != XrdOssOK)
479 {
480 TRACE(Error, tpfx << "Create failed for info file " << ifn << ERRNO_AND_ERRSTR(-res));
481 errno = -res;
482 m_data_file->Close(); delete m_data_file; m_data_file = 0;
483 if ( ! data_existed) myOss.Unlink(m_filename.c_str());
484 return false;
485 }
486
487 m_info_file = myOss.newFile(myUser);
488 if ((res = m_info_file->Open(ifn.c_str(), O_RDWR, 0600, myEnv)) != XrdOssOK)
489 {
490 TRACEF(Error, tpfx << "Failed for info file " << ifn << ERRNO_AND_ERRSTR(-res));
491 errno = -res;
492 delete m_info_file; m_info_file = 0;
493 m_data_file->Close(); delete m_data_file; m_data_file = 0;
494 // Remove only what this call created, so a failed open leaves no
495 // uncounted data/cinfo pair behind.
496 if ( ! info_existed) myOss.Unlink(ifn.c_str());
497 if ( ! data_existed) myOss.Unlink(m_filename.c_str());
498 return false;
499 }
500
501 bool initialize_info_file = true;
502
503 if (info_existed && m_cfi.Read(m_info_file, ifn.c_str()))
504 {
505 TRACEF(Debug, tpfx << "Reading existing info file. (data_existed=" << data_existed <<
506 ", data_size_stat=" << (data_existed ? data_stat.st_size : -1ll) <<
507 ", data_size_from_last_block=" << m_cfi.GetExpectedDataFileSize() <<
508 ", block_size=" << (m_cfi.GetBufferSize() >> 10) << "k)");
509
510 // Check if data file exists and is of reasonable size.
511 if (data_existed && data_stat.st_size >= m_cfi.GetExpectedDataFileSize())
512 {
513 initialize_info_file = false;
514 } else {
515 TRACEF(Warning, tpfx << "Basic sanity checks on data file failed, resetting info file, truncating data file.");
516 m_cfi.ResetAllAccessStats();
517 m_data_file->Ftruncate(0);
518 // data-file might not have existed at entry -- data_stat is then undefined
519 if (data_existed)
520 Cache::ResMon().register_file_purge(m_filename, data_stat.st_blocks);
521 }
522 }
523 else if (info_existed)
524 {
525 // The info file is about to be rewritten, so the cached blocks are lost:
526 // reset it like a rejected file.
527 TRACEF(Warning, tpfx << "Reading existing info file failed, resetting info file, truncating data file.");
528 m_cfi.ResetAllAccessStats();
529 m_data_file->Ftruncate(0);
530 if (data_existed)
531 Cache::ResMon().register_file_purge(m_filename, data_stat.st_blocks);
532 }
533
534 if ( ! initialize_info_file && m_cfi.GetCkSumState() != conf.get_cs_Chk())
535 {
536 if (conf.does_cschk_have_missing_bits(m_cfi.GetCkSumState()) &&
537 conf.should_uvkeep_purge(time(0) - m_cfi.GetNoCkSumTimeForUVKeep()))
538 {
539 TRACEF(Info, tpfx << "Cksum state of file insufficient, uvkeep test failed, resetting info file, truncating data file.");
540 initialize_info_file = true;
541 m_cfi.ResetAllAccessStats();
542 m_data_file->Ftruncate(0);
543 // data-file is known to exist due to checks in the previous if block
544 Cache::ResMon().register_file_purge(m_filename, data_stat.st_blocks);
545 } else {
546 // TODO: If the file is complete, we don't need to reset net cksums.
547 m_cfi.DowngradeCkSumState(conf.get_cs_Chk());
548 }
549 }
550
551 // Check if we have pfc url arguments.
552 long long pfc_blocksize = conf.m_bufferSize;
553 int pfc_prefetch = conf.m_prefetch_max_blocks;
555 {
556 parse_pfc_url_args(inputIO, pfc_blocksize, pfc_prefetch);
557 }
558
559 if (initialize_info_file)
560 {
561 m_cfi.SetBufferSizeFileSizeAndCreationTime(pfc_blocksize, m_file_size);
562 m_cfi.SetCkSumState(conf.get_cs_Chk());
563 m_cfi.ResetNoCkSumTime();
564 m_cfi.Write(m_info_file, ifn.c_str());
565 m_info_file->Fsync();
566 cache()->WriteFileSizeXAttr(m_info_file->getFD(), m_file_size);
567 // There is still a chance we had an orphaned data-file.
568 if (data_existed && ! info_existed) {
569 m_data_file->Ftruncate(0);
570 // Resource-monitor does not count orphaned files (planned for 6.x) so no register_purge().
571 }
572
573 if (cache()->RefConfiguration().m_httpcc)
574 {
575 std::string responseFctl;
576 int resFctl = inputIO->Fcntl(XrdOucCacheOp::Code::QFinfo, "head", responseFctl);
577 if (resFctl == 0)
578 {
579 std::string cc_str = responseFctl;
580 nlohmann::json cc_json = nlohmann::json::parse(cc_str);
581 if (cc_json.contains("max-age"))
582 {
583 time_t ma = cc_json["max-age"];
584 ma += time(NULL);
585 cc_json["expire"] = ma;
586 cc_str = cc_json.dump();
587 }
588 TRACE(Error, "GetFile() XrdCl::File::Fcntl value " << cc_str);
589 cache()->WriteCacheControlXAttr(m_info_file->getFD(), nullptr, cc_str);
590 }
591 else if (resFctl != kXR_Unsupported)
592 {
593 TRACE(Error, "GetFile() XrdCl::File::Fcntl query XrdCl::QueryCode::FInfo failed " << inputIO->Path());
594 }
595 }
596
597 TRACEF(Debug, tpfx << "Creating new file info, data size = " << m_file_size <<
598 " num blocks = " << m_cfi.GetNBlocks() <<
599 " block size = " << pfc_blocksize);
600 }
601 else
602 {
603 if (futimens(m_info_file->getFD(), NULL)) {
604 TRACEF(Error, tpfx << "failed setting modification time " << ERRNO_AND_ERRSTR(errno));
605 }
606 if (pfc_blocksize != conf.m_bufferSize) {
607 TRACEF(Info, tpfx << "URL CGI pfc.blocksize ignored for an already existing file");
608 }
609 }
610
611 m_cfi.WriteIOStatAttach();
612
613 // An open file must have a token, else its usage is never accounted.
614 m_resmon_token = Cache::ResMon().register_file_open(m_filename, time(0), ! initialize_info_file);
615 if (m_resmon_token < 0)
616 {
617 TRACEF(Error, tpfx << "no resource-monitor token available, not caching it; "
618 "this indicates a token leak in XrdPfc, please report it.");
619 m_info_file->Close(); delete m_info_file; m_info_file = nullptr;
620 m_data_file->Close(); delete m_data_file; m_data_file = nullptr;
621 // Remove only what this call created.
622 if ( ! info_existed) myOss.Unlink(ifn.c_str());
623 if ( ! data_existed) myOss.Unlink(m_filename.c_str());
624 errno = EMFILE;
625 return false;
626 }
627
628 // No m_state_cond: no other thread uses these members until GetFile() publishes the File.
629 m_block_size = m_cfi.GetBufferSize();
630 m_num_blocks = m_cfi.GetNBlocks();
631 m_prefetch_state = (m_cfi.IsComplete()) ? kComplete : kStopped; // Will engage in AddIO().
632 m_prefetch_max_blocks_in_flight = pfc_prefetch;
633 if (pfc_prefetch != conf.m_prefetch_max_blocks)
634 TRACEF(Debug, tpfx << "pfc.prefetch set to " << pfc_prefetch << " via CGI parameter");
635
636 m_data_file->Fstat(&data_stat);
637 m_st_blocks = data_stat.st_blocks;
638
639 constexpr long long MB = 1024 * 1024;
640 m_resmon_report_threshold = std::min(std::max(10 * MB, m_file_size / 20), 500 * MB);
641 // m_resmon_report_threshold_scaler; // something like 10% of original threshold, to adjust
642 // actual threshold based on return values from register_file_update_stats().
643
644 return true;
645}
646
647void File::parse_pfc_url_args(XrdOucCacheIO* inputIO, long long &pfc_blocksize, int &pfc_prefetch) const
648{
649 const Configuration &conf = Cache::TheOne().RefConfiguration();
650
651 XrdCl::URL url(inputIO->Path());
652 auto const & urlp = url.GetParams();
653
654 auto extract = [&](const std::string &key, std::string &value) -> bool {
655 auto it = urlp.find(key);
656 if (it != urlp.end()) {
657 value = it->second;
658 return true;
659 } else {
660 value.clear();
661 return false;
662 }
663 };
664
665 std::string val;
666 if (conf.m_cgi_blocksize_allowed && extract("pfc.blocksize", val))
667 {
668 const char *tpfx = "File::Open::urlcgi pfc.blocksize ";
669 long long bsize;
670 if (Cache::TheOne().blocksize_str2value(tpfx, val.c_str(), bsize,
672 {
673 pfc_blocksize = bsize;
674 } else {
675 TRACEF(Error, tpfx << "Error processing the parameter.");
676 }
677 }
678 if (conf.m_cgi_prefetch_allowed && extract("pfc.prefetch", val))
679 {
680 const char *tpfx = "File::Open::urlcgi pfc.prefetch ";
681 int pref;
682 if (Cache::TheOne().prefetch_str2value(tpfx, val.c_str(), pref,
684 {
685 pfc_prefetch = pref;
686 } else {
687 TRACEF(Error, tpfx << "Error processing the parameter.");
688 }
689 }
690}
691
692//------------------------------------------------------------------------------
693
694int File::Fstat(struct stat &sbuff)
695{
696 // Stat on an open file.
697 // Corrects size to actual full size of the file.
698 // Sets atime to 0 if the file is only partially downloaded, in accordance
699 // with pfc.onlyifcached settings.
700 // Called from IO::Fstat() and Cache::Stat() when the file is active.
701 // Returns 0 on success, -errno on error.
702
703 int res;
704
705 if ((res = m_data_file->Fstat(&sbuff))) return res;
706
707 sbuff.st_size = m_file_size;
708
709 bool is_cached = cache()->DecideIfConsideredCached(m_file_size, sbuff.st_blocks * 512ll);
710 if ( ! is_cached)
711 sbuff.st_atime = 0;
712
713 return 0;
714}
715
716//==============================================================================
717// Read and helpers
718//==============================================================================
719
720bool File::overlap(int blk, // block to query
721 long long blk_size, //
722 long long req_off, // offset of user request
723 int req_size, // size of user request
724 // output:
725 long long &off, // offset in user buffer
726 long long &blk_off, // offset in block
727 int &size) // size to copy
728{
729 const long long beg = blk * blk_size;
730 const long long end = beg + blk_size;
731 const long long req_end = req_off + req_size;
732
733 if (req_off < end && req_end > beg)
734 {
735 const long long ovlp_beg = std::max(beg, req_off);
736 const long long ovlp_end = std::min(end, req_end);
737
738 off = ovlp_beg - req_off;
739 blk_off = ovlp_beg - beg;
740 size = (int) (ovlp_end - ovlp_beg);
741
742 assert(size <= blk_size);
743 return true;
744 }
745 else
746 {
747 return false;
748 }
749}
750
751//------------------------------------------------------------------------------
752
753Block* File::PrepareBlockRequest(int i, IO *io, void *req_id, bool prefetch)
754{
755 // Must be called w/ state_cond locked.
756 // Checks on size etc should be done before.
757 //
758 // Reference count is 0 so increase it in calling function if you want to
759 // catch the block while still in memory.
760
761 const long long off = i * m_block_size;
762 const int last_block = m_num_blocks - 1;
763 const bool cs_net = cache()->RefConfiguration().is_cschk_net();
764
765 int blk_size, req_size;
766 if (i == last_block) {
767 blk_size = req_size = m_file_size - off;
768 if (cs_net && req_size & 0xFFF) req_size = (req_size & ~0xFFF) + 0x1000;
769 } else {
770 blk_size = req_size = m_block_size;
771 }
772
773 Block *b = 0;
774 char *buf = cache()->RequestRAM(req_size);
775
776 if (buf)
777 {
778 b = new (std::nothrow) Block(this, io, req_id, buf, off, blk_size, req_size, prefetch, cs_net);
779
780 if (b)
781 {
782 m_block_map[i] = b;
783
784 // Actual Read request is issued in ProcessBlockRequests().
785
786 if (m_prefetch_state == kOn && (int) m_block_map.size() >= m_prefetch_max_blocks_in_flight)
787 {
788 m_prefetch_state = kHold;
789 cache()->DeRegisterPrefetchFile(this);
790 }
791 }
792 else
793 {
794 TRACEF(Dump, "PrepareBlockRequest() " << i << " prefetch " << prefetch << ", allocation failed.");
795 }
796 }
797
798 return b;
799}
800
801void File::ProcessBlockRequest(Block *b)
802{
803 // This *must not* be called with block_map locked.
804
806
807 if (XRD_TRACE What >= TRACE_Dump) {
808 char buf[256];
809 snprintf(buf, 256, "idx=%lld, block=%p, prefetch=%d, off=%lld, req_size=%d, buff=%p, resp_handler=%p ",
810 b->get_offset()/m_block_size, (void*)b, b->m_prefetch, b->get_offset(), b->get_req_size(), (void*)b->get_buff(), (void*)brh);
811 TRACEF(Dump, "ProcessBlockRequest() " << buf);
812 }
813
814 if (b->req_cksum_net())
815 {
816 b->get_io()->GetInput()->pgRead(*brh, b->get_buff(), b->get_offset(), b->get_req_size(),
817 b->ref_cksum_vec(), 0, b->ptr_n_cksum_errors());
818 } else {
819 b->get_io()->GetInput()-> Read(*brh, b->get_buff(), b->get_offset(), b->get_size());
820 }
821}
822
823void File::ProcessBlockRequests(BlockList_t& blks)
824{
825 // This *must not* be called with block_map locked.
826
827 for (BlockList_i bi = blks.begin(); bi != blks.end(); ++bi)
828 {
829 ProcessBlockRequest(*bi);
830 }
831}
832
833//------------------------------------------------------------------------------
834
835void File::RequestBlocksDirect(IO *io, ReadRequest *read_req, std::vector<XrdOucIOVec>& ioVec, int expected_size)
836{
837 int n_chunks = ioVec.size();
838 int n_vec_reads = (n_chunks - 1) / XrdProto::maxRvecsz + 1;
839
840 TRACEF(DumpXL, "RequestBlocksDirect() issuing ReadV for n_chunks = " << n_chunks <<
841 ", total_size = " << expected_size << ", n_vec_reads = " << n_vec_reads);
842
843 DirectResponseHandler *handler = new DirectResponseHandler(this, read_req, n_vec_reads);
844
845 int pos = 0;
846 while (n_chunks > XrdProto::maxRvecsz) {
847 io->GetInput()->ReadV( *handler, ioVec.data() + pos, XrdProto::maxRvecsz);
848 pos += XrdProto::maxRvecsz;
849 n_chunks -= XrdProto::maxRvecsz;
850 }
851 io->GetInput()->ReadV( *handler, ioVec.data() + pos, n_chunks);
852}
853
854//------------------------------------------------------------------------------
855
856int File::ReadBlocksFromDisk(std::vector<XrdOucIOVec>& ioVec, int expected_size)
857{
858 TRACEF(DumpXL, "ReadBlocksFromDisk() issuing ReadV for n_chunks = " << (int) ioVec.size() << ", total_size = " << expected_size);
859
860 long long rs = m_data_file->ReadV(ioVec.data(), (int) ioVec.size());
861
862 if (rs < 0)
863 {
864 TRACEF(Error, "ReadBlocksFromDisk neg retval = " << rs);
865 return rs;
866 }
867
868 if (rs != expected_size)
869 {
870 TRACEF(Error, "ReadBlocksFromDisk incomplete size = " << rs);
871 return -EIO;
872 }
873
874 return (int) rs;
875}
876
877//------------------------------------------------------------------------------
878
879int File::Read(IO *io, char* iUserBuff, long long iUserOff, int iUserSize, ReadReqRH *rh)
880{
881 // rrc_func is ONLY called from async processing.
882 // If this function returns anything other than kReadPending, rrc_func needs to be called by the caller.
883 // This streamlines implementation of synchronous IO::Read().
884
885 TRACEF(Dump, "Read() sid: " << Xrd::hex1 << rh->m_seq_id << " size: " << iUserSize);
886
887 m_state_cond.Lock();
888
889 if (m_in_shutdown || io->m_in_detach)
890 {
891 m_state_cond.UnLock();
892 return m_in_shutdown ? -ENOENT : -EBADF;
893 }
894
895 // Shortcut -- file is fully downloaded.
896
897 if (m_cfi.IsComplete())
898 {
899 m_state_cond.UnLock();
900 int ret = m_data_file->Read(iUserBuff, iUserOff, iUserSize);
901 if (ret > 0) {
902 XrdSysCondVarHelper _lck(m_state_cond);
903 m_delta_stats.AddBytesHit(ret);
904 check_delta_stats();
905 }
906 return ret;
907 }
908
909 XrdOucIOVec readV( { iUserOff, iUserSize, 0, iUserBuff } );
910
911 return ReadOpusCoalescere(io, &readV, 1, rh, "Read() ");
912}
913
914//------------------------------------------------------------------------------
915
916int File::ReadV(IO *io, const XrdOucIOVec *readV, int readVnum, ReadReqRH *rh)
917{
918 TRACEF(Dump, "ReadV() for " << readVnum << " chunks.");
919
920 m_state_cond.Lock();
921
922 if (m_in_shutdown || io->m_in_detach)
923 {
924 m_state_cond.UnLock();
925 return m_in_shutdown ? -ENOENT : -EBADF;
926 }
927
928 // Shortcut -- file is fully downloaded.
929
930 if (m_cfi.IsComplete())
931 {
932 m_state_cond.UnLock();
933 int ret = m_data_file->ReadV(const_cast<XrdOucIOVec*>(readV), readVnum);
934 if (ret > 0) {
935 XrdSysCondVarHelper _lck(m_state_cond);
936 m_delta_stats.AddBytesHit(ret);
937 check_delta_stats();
938 }
939 return ret;
940 }
941
942 return ReadOpusCoalescere(io, readV, readVnum, rh, "ReadV() ");
943}
944
945//------------------------------------------------------------------------------
946
947int File::ReadOpusCoalescere(IO *io, const XrdOucIOVec *readV, int readVnum,
948 ReadReqRH *rh, const char *tpfx)
949{
950 // Non-trivial processing for Read and ReadV.
951 // Entered under lock.
952 //
953 // loop over reqired blocks:
954 // - if on disk, ok;
955 // - if in ram or incoming, inc ref-count
956 // - otherwise request and inc ref count (unless RAM full => request direct)
957 // unlock
958
959 int prefetch_cnt = 0;
960
961 ReadRequest *read_req = nullptr;
962 BlockList_t blks_to_request; // blocks we are issuing a new remote request for
963
964 std::unordered_map<Block*, std::vector<ChunkRequest>> blks_ready;
965
966 std::vector<XrdOucIOVec> iovec_disk;
967 std::vector<XrdOucIOVec> iovec_direct;
968 int iovec_disk_total = 0;
969 int iovec_direct_total = 0;
970
971 for (int iov_idx = 0; iov_idx < readVnum; ++iov_idx)
972 {
973 const XrdOucIOVec &iov = readV[iov_idx];
974 long long iUserOff = iov.offset;
975 int iUserSize = iov.size;
976 char *iUserBuff = iov.data;
977
978 const int idx_first = iUserOff / m_block_size;
979 const int idx_last = (iUserOff + iUserSize - 1) / m_block_size;
980
981 TRACEF(DumpXL, tpfx << "sid: " << Xrd::hex1 << rh->m_seq_id << " idx_first: " << idx_first << " idx_last: " << idx_last);
982
983 enum LastBlock_e { LB_other, LB_disk, LB_direct };
984
985 LastBlock_e lbe = LB_other;
986
987 for (int block_idx = idx_first; block_idx <= idx_last; ++block_idx)
988 {
989 TRACEF(DumpXL, tpfx << "sid: " << Xrd::hex1 << rh->m_seq_id << " idx: " << block_idx);
990 BlockMap_i bi = m_block_map.find(block_idx);
991
992 // overlap and read
993 long long off = 0; // offset in user buffer
994 long long blk_off = 0; // offset in block
995 int size = 0; // size to copy
996
997 overlap(block_idx, m_block_size, iUserOff, iUserSize, off, blk_off, size);
998
999 // In RAM or incoming?
1000 if (bi != m_block_map.end())
1001 {
1002 inc_ref_count(bi->second);
1003 TRACEF(Dump, tpfx << (void*) iUserBuff << " inc_ref_count for existing block " << bi->second << " idx = " << block_idx);
1004
1005 if (bi->second->is_finished())
1006 {
1007 // note, blocks with error should not be here !!!
1008 // they should be either removed or reissued in ProcessBlockResponse()
1009 assert(bi->second->is_ok());
1010
1011 blks_ready[bi->second].emplace_back( ChunkRequest(nullptr, iUserBuff + off, blk_off, size) );
1012
1013 if (bi->second->m_prefetch)
1014 ++prefetch_cnt;
1015 }
1016 else
1017 {
1018 if ( ! read_req)
1019 read_req = new ReadRequest(io, rh);
1020
1021 // We have a lock on state_cond --> as we register the request before releasing the lock,
1022 // we are sure to get a call-in via the ChunkRequest handling when this block arrives.
1023
1024 bi->second->m_chunk_reqs.emplace_back( ChunkRequest(read_req, iUserBuff + off, blk_off, size) );
1025 ++read_req->m_n_chunk_reqs;
1026 }
1027
1028 lbe = LB_other;
1029 }
1030 // On disk?
1031 else if (m_cfi.TestBitWritten(offsetIdx(block_idx)))
1032 {
1033 TRACEF(DumpXL, tpfx << "read from disk " << (void*)iUserBuff << " idx = " << block_idx);
1034
1035 if (lbe == LB_disk)
1036 iovec_disk.back().size += size;
1037 else
1038 iovec_disk.push_back( { block_idx * m_block_size + blk_off, size, 0, iUserBuff + off } );
1039 iovec_disk_total += size;
1040
1041 if (m_cfi.TestBitPrefetch(offsetIdx(block_idx)))
1042 ++prefetch_cnt;
1043
1044 lbe = LB_disk;
1045 }
1046 // Neither ... then we have to go get it ...
1047 else
1048 {
1049 if ( ! read_req)
1050 read_req = new ReadRequest(io, rh);
1051
1052 // Is there room for one more RAM Block?
1053 Block *b = PrepareBlockRequest(block_idx, io, read_req, false);
1054 if (b)
1055 {
1056 TRACEF(Dump, tpfx << "inc_ref_count new " << (void*)iUserBuff << " idx = " << block_idx);
1057 inc_ref_count(b);
1058 blks_to_request.push_back(b);
1059
1060 b->m_chunk_reqs.emplace_back(ChunkRequest(read_req, iUserBuff + off, blk_off, size));
1061 ++read_req->m_n_chunk_reqs;
1062
1063 lbe = LB_other;
1064 }
1065 else // Nope ... read this directly without caching.
1066 {
1067 TRACEF(DumpXL, tpfx << "direct block " << block_idx << ", blk_off " << blk_off << ", size " << size);
1068
1069 iovec_direct_total += size;
1070 read_req->m_direct_done = false;
1071
1072 // Make sure we do not issue a ReadV with chunk size above XrdProto::maxRVdsz.
1073 // Number of actual ReadVs issued so as to not exceed the XrdProto::maxRvecsz limit
1074 // is determined in the RequestBlocksDirect().
1075 if (lbe == LB_direct && iovec_direct.back().size + size <= XrdProto::maxRVdsz) {
1076 iovec_direct.back().size += size;
1077 } else {
1078 long long in_offset = block_idx * m_block_size + blk_off;
1079 char *out_pos = iUserBuff + off;
1080 while (size > XrdProto::maxRVdsz) {
1081 iovec_direct.push_back( { in_offset, XrdProto::maxRVdsz, 0, out_pos } );
1082 in_offset += XrdProto::maxRVdsz;
1083 out_pos += XrdProto::maxRVdsz;
1084 size -= XrdProto::maxRVdsz;
1085 }
1086 iovec_direct.push_back( { in_offset, size, 0, out_pos } );
1087 }
1088
1089 lbe = LB_direct;
1090 }
1091 }
1092 } // end for over blocks in an IOVec
1093 } // end for over readV IOVec
1094
1095 inc_prefetch_hit_cnt(prefetch_cnt);
1096
1097 m_state_cond.UnLock();
1098
1099 // First, send out remote requests for new blocks.
1100 if ( ! blks_to_request.empty())
1101 {
1102 ProcessBlockRequests(blks_to_request);
1103 blks_to_request.clear();
1104 }
1105
1106 // Second, send out remote direct read requests.
1107 if ( ! iovec_direct.empty())
1108 {
1109 RequestBlocksDirect(io, read_req, iovec_direct, iovec_direct_total);
1110
1111 TRACEF(Dump, tpfx << "direct read requests sent out, n_chunks = " << (int) iovec_direct.size() << ", total_size = " << iovec_direct_total);
1112 }
1113
1114 // Begin synchronous part where we process data that is already in RAM or on disk.
1115
1116 long long bytes_read = 0;
1117 int error_cond = 0; // to be set to -errno
1118
1119 // Third, process blocks that are available in RAM.
1120 if ( ! blks_ready.empty())
1121 {
1122 for (auto &bvi : blks_ready)
1123 {
1124 for (auto &cr : bvi.second)
1125 {
1126 TRACEF(DumpXL, tpfx << "ub=" << (void*)cr.m_buf << " from pre-finished block " << bvi.first->m_offset/m_block_size << " size " << cr.m_size);
1127 memcpy(cr.m_buf, bvi.first->m_buff + cr.m_off, cr.m_size);
1128 bytes_read += cr.m_size;
1129 }
1130 }
1131 }
1132
1133 // Fourth, read blocks from disk.
1134 if ( ! iovec_disk.empty())
1135 {
1136 int rc = ReadBlocksFromDisk(iovec_disk, iovec_disk_total);
1137 TRACEF(DumpXL, tpfx << "from disk finished size = " << rc);
1138 if (rc >= 0)
1139 {
1140 bytes_read += rc;
1141 }
1142 else
1143 {
1144 error_cond = rc;
1145 TRACEF(Error, tpfx << "failed read from disk");
1146 }
1147 }
1148
1149 // End synchronous part -- update with sync stats and determine actual state of this read.
1150 // Note: remote reads might have already finished during disk-read!
1151
1152 m_state_cond.Lock();
1153
1154 for (auto &bvi : blks_ready)
1155 dec_ref_count(bvi.first, (int) bvi.second.size());
1156
1157 if (read_req)
1158 {
1159 read_req->m_bytes_read += bytes_read;
1160 if (error_cond)
1161 read_req->update_error_cond(error_cond);
1162 read_req->m_stats.m_BytesHit += bytes_read;
1163 read_req->m_sync_done = true;
1164
1165 if (read_req->is_complete())
1166 {
1167 // Almost like FinalizeReadRequest(read_req) -- but no callout!
1168 m_delta_stats.AddReadStats(read_req->m_stats);
1169 check_delta_stats();
1170 m_state_cond.UnLock();
1171
1172 int ret = read_req->return_value();
1173 delete read_req;
1174 return ret;
1175 }
1176 else
1177 {
1178 m_state_cond.UnLock();
1179 return kReadPending;
1180 }
1181 }
1182 else
1183 {
1184 m_delta_stats.m_BytesHit += bytes_read;
1185 check_delta_stats();
1186 m_state_cond.UnLock();
1187
1188 // !!! No callout.
1189
1190 return error_cond ? error_cond : bytes_read;
1191 }
1192}
1193
1194
1195//==============================================================================
1196// WriteBlock and Sync
1197//==============================================================================
1198
1200{
1201 // write block buffer into disk file
1202 long long offset = b->m_offset - m_offset;
1203 long long size = b->get_size();
1204 ssize_t retval;
1205
1206 if (m_cfi.IsCkSumCache())
1207 if (b->has_cksums())
1208 retval = m_data_file->pgWrite(b->get_buff(), offset, size, b->ref_cksum_vec().data(), 0);
1209 else
1210 retval = m_data_file->pgWrite(b->get_buff(), offset, size, 0, 0);
1211 else
1212 retval = m_data_file->Write(b->get_buff(), offset, size);
1213
1214 if (retval < size)
1215 {
1216 if (retval < 0) {
1217 TRACEF(Error, "WriteToDisk() write error " << retval);
1218 } else {
1219 TRACEF(Error, "WriteToDisk() incomplete block write ret=" << retval << " (should be " << size << ")");
1220 }
1221
1222 XrdSysCondVarHelper _lck(m_state_cond);
1223
1224 dec_ref_count(b);
1225
1226 return;
1227 }
1228
1229 const int blk_idx = (b->m_offset - m_offset) / m_block_size;
1230
1231 // Set written bit.
1232 TRACEF(Dump, "WriteToDisk() success set bit for block " << b->m_offset << " size=" << size);
1233
1234 bool schedule_sync = false;
1235 {
1236 XrdSysCondVarHelper _lck(m_state_cond);
1237
1238 m_cfi.SetBitWritten(blk_idx);
1239
1240 if (b->m_prefetch)
1241 {
1242 m_cfi.SetBitPrefetch(blk_idx);
1243 }
1244 if (b->req_cksum_net() && ! b->has_cksums() && m_cfi.IsCkSumNet())
1245 {
1246 m_cfi.ResetCkSumNet();
1247 }
1248
1249 // Set synced bit or stash block index if in actual sync.
1250 // Synced state is only written out to cinfo file when data file is synced.
1251 if (m_in_sync)
1252 {
1253 m_writes_during_sync.push_back(blk_idx);
1254 }
1255 else
1256 {
1257 m_cfi.SetBitSynced(blk_idx);
1258 ++m_non_flushed_cnt;
1259 if ((m_cfi.IsComplete() || m_non_flushed_cnt >= Cache::GetInstance().RefConfiguration().m_flushCnt) &&
1260 ! m_in_shutdown)
1261 {
1262 schedule_sync = true;
1263 m_in_sync = true;
1264 m_non_flushed_cnt = 0;
1265 }
1266 }
1267 // As soon as the reference count is decreased on the block, the
1268 // file object may be deleted. Thus, to avoid holding both locks at a time,
1269 // we defer the ref count decrease until later if a sync is needed
1270 if (!schedule_sync) {
1271 dec_ref_count(b);
1272 }
1273 }
1274
1275 if (schedule_sync)
1276 {
1277 cache()->ScheduleFileSync(this);
1278 XrdSysCondVarHelper _lck(m_state_cond);
1279 dec_ref_count(b);
1280 }
1281}
1282
1283//------------------------------------------------------------------------------
1284
1286{
1287 TRACEF(Dump, "Sync()");
1288
1289 int ret = m_data_file->Fsync();
1290 bool errorp = false;
1291 if (ret == XrdOssOK)
1292 {
1293 Stats loc_stats;
1294 {
1295 XrdSysCondVarHelper _lck(&m_state_cond);
1296 report_and_merge_delta_stats();
1297 loc_stats = m_stats;
1298 }
1299 m_cfi.WriteIOStat(loc_stats);
1300 m_cfi.Write(m_info_file, m_filename.c_str());
1301 int cret = m_info_file->Fsync();
1302 if (cret != XrdOssOK)
1303 {
1304 TRACEF(Error, "Sync cinfo file sync error " << cret);
1305 errorp = true;
1306 }
1307 }
1308 else
1309 {
1310 TRACEF(Error, "Sync data file sync error " << ret << ", cinfo file has not been updated");
1311 errorp = true;
1312 }
1313
1314 if (errorp)
1315 {
1316 TRACEF(Error, "Sync failed, unlinking local files and initiating shutdown of File object");
1317
1318 // Unlink will also call this->initiate_emergency_shutdown()
1319 Cache::GetInstance().UnlinkFile(m_filename, false);
1320
1321 XrdSysCondVarHelper _lck(&m_state_cond);
1322
1323 m_writes_during_sync.clear();
1324 m_in_sync = false;
1325
1326 return;
1327 }
1328
1329 int written_while_in_sync;
1330 bool resync = false;
1331 {
1332 XrdSysCondVarHelper _lck(&m_state_cond);
1333 for (std::vector<int>::iterator i = m_writes_during_sync.begin(); i != m_writes_during_sync.end(); ++i)
1334 {
1335 m_cfi.SetBitSynced(*i);
1336 }
1337 written_while_in_sync = m_non_flushed_cnt = (int) m_writes_during_sync.size();
1338 m_writes_during_sync.clear();
1339
1340 // If there were writes during sync and the file is now complete,
1341 // let us call Sync again without resetting the m_in_sync flag.
1342 if (written_while_in_sync > 0 && m_cfi.IsComplete() && ! m_in_shutdown)
1343 resync = true;
1344 else
1345 m_in_sync = false;
1346 }
1347 TRACEF(Dump, "Sync "<< written_while_in_sync << " blocks written during sync." << (resync ? " File is now complete - resyncing." : ""));
1348
1349 if (resync)
1350 Sync();
1351}
1352
1353
1354//==============================================================================
1355// Block processing
1356//==============================================================================
1357
1358void File::free_block(Block* b)
1359{
1360 // Method always called under lock.
1361 int i = b->m_offset / m_block_size;
1362 TRACEF(Dump, "free_block block " << b << " idx = " << i);
1363 size_t ret = m_block_map.erase(i);
1364 if (ret != 1)
1365 {
1366 // assert might be a better option than a warning
1367 TRACEF(Error, "free_block did not erase " << i << " from map");
1368 }
1369 else
1370 {
1371 cache()->ReleaseRAM(b->m_buff, b->m_req_size);
1372 delete b;
1373 }
1374
1375 if (m_prefetch_state == kHold && (int) m_block_map.size() < m_prefetch_max_blocks_in_flight)
1376 {
1377 m_prefetch_state = kOn;
1378 cache()->RegisterPrefetchFile(this);
1379 }
1380}
1381
1382//------------------------------------------------------------------------------
1383
1384bool File::select_current_io_or_disable_prefetching(bool skip_current)
1385{
1386 // Method always called under lock. It also expects prefetch to be active.
1387
1388 int io_size = (int) m_io_set.size();
1389 bool io_ok = false;
1390
1391 if (io_size == 1)
1392 {
1393 io_ok = (*m_io_set.begin())->m_allow_prefetching;
1394 if (io_ok)
1395 {
1396 m_current_io = m_io_set.begin();
1397 }
1398 }
1399 else if (io_size > 1)
1400 {
1401 IoSet_i mi = m_current_io;
1402 if (skip_current && mi != m_io_set.end()) ++mi;
1403
1404 for (int i = 0; i < io_size; ++i)
1405 {
1406 if (mi == m_io_set.end()) mi = m_io_set.begin();
1407
1408 if ((*mi)->m_allow_prefetching)
1409 {
1410 m_current_io = mi;
1411 io_ok = true;
1412 break;
1413 }
1414 ++mi;
1415 }
1416 }
1417
1418 if ( ! io_ok)
1419 {
1420 m_current_io = m_io_set.end();
1421 m_prefetch_state = kStopped;
1422 cache()->DeRegisterPrefetchFile(this);
1423 }
1424
1425 return io_ok;
1426}
1427
1428//------------------------------------------------------------------------------
1429
1430void File::ProcessDirectReadFinished(ReadRequest *rreq, int bytes_read, int error_cond)
1431{
1432 // Called from DirectResponseHandler.
1433 // NOT under lock.
1434
1435 if (error_cond)
1436 TRACEF(Error, "Read(), direct read finished with error " << -error_cond << " " << XrdSysE2T(-error_cond));
1437
1438 m_state_cond.Lock();
1439
1440 if (error_cond)
1441 rreq->update_error_cond(error_cond);
1442 else {
1443 rreq->m_stats.m_BytesBypassed += bytes_read;
1444 rreq->m_bytes_read += bytes_read;
1445 }
1446
1447 rreq->m_direct_done = true;
1448
1449 bool rreq_complete = rreq->is_complete();
1450
1451 m_state_cond.UnLock();
1452
1453 if (rreq_complete)
1454 FinalizeReadRequest(rreq);
1455}
1456
1457void File::ProcessBlockError(Block *b, ReadRequest *rreq)
1458{
1459 // Called from ProcessBlockResponse().
1460 // YES under lock -- we have to protect m_block_map for recovery through multiple IOs.
1461 // Does not manage m_read_req.
1462 // Will not complete the request.
1463
1464 TRACEF(Debug, "ProcessBlockError() io " << b->m_io << ", block "<< b->m_offset/m_block_size <<
1465 " finished with error " << -b->get_error() << " " << XrdSysE2T(-b->get_error()));
1466
1467 rreq->update_error_cond(b->get_error());
1468 --rreq->m_n_chunk_reqs;
1469
1470 dec_ref_count(b);
1471}
1472
1473void File::ProcessBlockSuccess(Block *b, ChunkRequest &creq)
1474{
1475 // Called from ProcessBlockResponse().
1476 // NOT under lock as it does memcopy ofor exisf block data.
1477 // Acquires lock for block, m_read_req and rreq state update.
1478
1479 ReadRequest *rreq = creq.m_read_req;
1480
1481 TRACEF(Dump, "ProcessBlockSuccess() ub=" << (void*)creq.m_buf << " from finished block " << b->m_offset/m_block_size << " size " << creq.m_size);
1482 memcpy(creq.m_buf, b->m_buff + creq.m_off, creq.m_size);
1483
1484 m_state_cond.Lock();
1485
1486 rreq->m_bytes_read += creq.m_size;
1487
1488 if (b->get_req_id() == (void*) rreq)
1489 rreq->m_stats.m_BytesMissed += creq.m_size;
1490 else
1491 rreq->m_stats.m_BytesHit += creq.m_size;
1492
1493 --rreq->m_n_chunk_reqs;
1494
1495 if (b->m_prefetch)
1496 inc_prefetch_hit_cnt(1);
1497
1498 dec_ref_count(b);
1499
1500 bool rreq_complete = rreq->is_complete();
1501
1502 m_state_cond.UnLock();
1503
1504 if (rreq_complete)
1505 FinalizeReadRequest(rreq);
1506}
1507
1508void File::FinalizeReadRequest(ReadRequest *rreq)
1509{
1510 // called from ProcessBlockResponse()
1511 // NOT under lock -- does callout
1512 {
1513 XrdSysCondVarHelper _lck(m_state_cond);
1514 m_delta_stats.AddReadStats(rreq->m_stats);
1515 check_delta_stats();
1516 }
1517
1518 rreq->m_rh->Done(rreq->return_value());
1519 delete rreq;
1520}
1521
1522void File::ProcessBlockResponse(Block *b, int res)
1523{
1524 static const char* tpfx = "ProcessBlockResponse ";
1525
1526 TRACEF(Dump, tpfx << "block=" << b << ", idx=" << b->m_offset/m_block_size << ", off=" << b->m_offset << ", res=" << res);
1527
1528 if (res >= 0 && res != b->get_size())
1529 {
1530 // Incorrect number of bytes received, apparently size of the file on the remote
1531 // is different than what the cache expects it to be.
1532 TRACEF(Error, tpfx << "Wrong number of bytes received, assuming remote/local file size mismatch, unlinking local files and initiating shutdown of File object");
1533 Cache::GetInstance().UnlinkFile(m_filename, false);
1534 }
1535
1536 m_state_cond.Lock();
1537
1538 // Deregister block from IO's prefetch count, if needed.
1539 if (b->m_prefetch)
1540 {
1541 IO *io = b->get_io();
1542 IoSet_i mi = m_io_set.find(io);
1543 if (mi != m_io_set.end())
1544 {
1545 --io->m_active_prefetches;
1546
1547 // If failed and IO is still prefetching -- disable prefetching on this IO.
1548 if (res < 0 && io->m_allow_prefetching)
1549 {
1550 TRACEF(Debug, tpfx << "after failed prefetch on io " << io << " disabling prefetching on this io.");
1551 io->m_allow_prefetching = false;
1552
1553 // Check if any IO is still available for prfetching. If not, stop it.
1554 if (m_prefetch_state == kOn || m_prefetch_state == kHold)
1555 {
1556 if ( ! select_current_io_or_disable_prefetching(false) )
1557 {
1558 TRACEF(Debug, tpfx << "stopping prefetching after io " << b->get_io() << " marked as bad.");
1559 }
1560 }
1561 }
1562
1563 // If failed with no subscribers -- delete the block and exit.
1564 if (b->m_refcnt == 0 && (res < 0 || m_in_shutdown))
1565 {
1566 free_block(b);
1567 m_state_cond.UnLock();
1568 return;
1569 }
1570 m_prefetch_bytes += b->get_size();
1571 }
1572 else
1573 {
1574 TRACEF(Error, tpfx << "io " << b->get_io() << " not found in IoSet.");
1575 }
1576 }
1577
1578 if (res == b->get_size())
1579 {
1580 b->set_downloaded();
1581 TRACEF(Dump, tpfx << "inc_ref_count idx=" << b->m_offset/m_block_size);
1582 if ( ! m_in_shutdown)
1583 {
1584 // Increase ref-count for the writer.
1585 inc_ref_count(b);
1586 m_delta_stats.AddWriteStats(b->get_size(), b->get_n_cksum_errors());
1587 // No check for writes, report-and-merge forced during Sync().
1588 cache()->AddWriteTask(b, true);
1589 }
1590
1591 // Swap chunk-reqs vector out of Block, it will be processed outside of lock.
1592 vChunkRequest_t creqs_to_notify;
1593 creqs_to_notify.swap( b->m_chunk_reqs );
1594
1595 m_state_cond.UnLock();
1596
1597 for (auto &creq : creqs_to_notify)
1598 {
1599 ProcessBlockSuccess(b, creq);
1600 }
1601 }
1602 else
1603 {
1604 if (res < 0) {
1605 bool new_error = b->get_io()->register_block_error(res);
1606 int tlvl = new_error ? TRACE_Error : TRACE_Debug;
1607 TRACEF_INT(tlvl, tpfx << "block " << b << ", idx=" << b->m_offset/m_block_size << ", off=" << b->m_offset
1608 << ", io=" << b->get_io() << ", error=" << res);
1609 } else {
1610 bool first_p = b->get_io()->register_incomplete_read();
1611 int tlvl = first_p ? TRACE_Error : TRACE_Debug;
1612 TRACEF_INT(tlvl, tpfx << "block " << b << ", idx=" << b->m_offset/m_block_size << ", off=" << b->m_offset
1613 << ", io=" << b->get_io() << " incomplete, got " << res << " expected " << b->get_size());
1614#if defined(__APPLE__) || defined(__GNU__) || (defined(__FreeBSD_kernel__) && defined(__GLIBC__)) || defined(__FreeBSD__)
1615 res = -EIO;
1616#else
1617 res = -EREMOTEIO;
1618#endif
1619 }
1620 b->set_error(res);
1621
1622 // Loop over Block's chunk-reqs vector, error out ones with the same IO.
1623 // Collect others with a different IO, the first of them will be used to reissue the request.
1624 // This is then done outside of lock.
1625 std::list<ReadRequest*> rreqs_to_complete;
1626 vChunkRequest_t creqs_to_keep;
1627
1628 for(ChunkRequest &creq : b->m_chunk_reqs)
1629 {
1630 ReadRequest *rreq = creq.m_read_req;
1631
1632 if (rreq->m_io == b->get_io())
1633 {
1634 ProcessBlockError(b, rreq);
1635 if (rreq->is_complete())
1636 {
1637 rreqs_to_complete.push_back(rreq);
1638 }
1639 }
1640 else
1641 {
1642 creqs_to_keep.push_back(creq);
1643 }
1644 }
1645
1646 bool reissue = false;
1647 if ( ! creqs_to_keep.empty())
1648 {
1649 ReadRequest *rreq = creqs_to_keep.front().m_read_req;
1650
1651 TRACEF(Debug, "ProcessBlockResponse() requested block " << (void*)b << " failed with another io " <<
1652 b->get_io() << " - reissuing request with my io " << rreq->m_io);
1653
1654 b->reset_error_and_set_io(rreq->m_io, rreq);
1655 b->m_chunk_reqs.swap( creqs_to_keep );
1656 reissue = true;
1657 }
1658
1659 m_state_cond.UnLock();
1660
1661 for (auto rreq : rreqs_to_complete)
1662 FinalizeReadRequest(rreq);
1663
1664 if (reissue)
1665 ProcessBlockRequest(b);
1666 }
1667}
1668
1669//------------------------------------------------------------------------------
1670
1671const char* File::lPath() const
1672{
1673 return m_filename.c_str();
1674}
1675
1676//------------------------------------------------------------------------------
1677
1678int File::offsetIdx(int iIdx) const
1679{
1680 return iIdx - m_offset/m_block_size;
1681}
1682
1683
1684//------------------------------------------------------------------------------
1685
1687{
1688 // Check that block is not on disk and not in RAM.
1689 // TODO: Could prefetch several blocks at once!
1690 // blks_max could be an argument
1691
1692 BlockList_t blks;
1693
1694 TRACEF(DumpXL, "Prefetch() entering.");
1695 {
1696 XrdSysCondVarHelper _lck(m_state_cond);
1697
1698 if (m_prefetch_state != kOn)
1699 {
1700 return;
1701 }
1702
1703 if ( ! select_current_io_or_disable_prefetching(true) )
1704 {
1705 TRACEF(Error, "Prefetch no available IO object found, prefetching stopped. This should not happen, i.e., prefetching should be stopped before.");
1706 return;
1707 }
1708
1709 // Select block(s) to fetch.
1710 for (int f = 0; f < m_num_blocks; ++f)
1711 {
1712 if ( ! m_cfi.TestBitWritten(f))
1713 {
1714 int f_act = f + m_offset / m_block_size;
1715
1716 BlockMap_i bi = m_block_map.find(f_act);
1717 if (bi == m_block_map.end())
1718 {
1719 Block *b = PrepareBlockRequest(f_act, *m_current_io, nullptr, true);
1720 if (b)
1721 {
1722 TRACEF(Dump, "Prefetch take block " << f_act);
1723 blks.push_back(b);
1724 // Note: block ref_cnt not increased, it will be when placed into write queue.
1725
1726 inc_prefetch_read_cnt(1);
1727 }
1728 else
1729 {
1730 // This shouldn't happen as prefetching stops when RAM is 70% full.
1731 TRACEF(Warning, "Prefetch allocation failed for block " << f_act);
1732 }
1733 break;
1734 }
1735 }
1736 }
1737
1738 if (blks.empty())
1739 {
1740 TRACEF(Debug, "Prefetch file is complete, stopping prefetch.");
1741 m_prefetch_state = kComplete;
1742 cache()->DeRegisterPrefetchFile(this);
1743 }
1744 else
1745 {
1746 (*m_current_io)->m_active_prefetches += (int) blks.size();
1747 }
1748 }
1749
1750 if ( ! blks.empty())
1751 {
1752 ProcessBlockRequests(blks);
1753 }
1754}
1755
1756
1757//------------------------------------------------------------------------------
1758
1760{
1761 return m_prefetch_score;
1762}
1763
1765{
1766 return Cache::TheOne().GetLog();
1767}
1768
1770{
1771 return Cache::TheOne().GetTrace();
1772}
1773
1774void File::insert_remote_location(const std::string &loc)
1775{
1776 if ( ! loc.empty())
1777 {
1778 size_t p = loc.find_first_of('@');
1779 m_remote_locations.insert(&loc[p != std::string::npos ? p + 1 : 0]);
1780 }
1781}
1782
1783std::string File::GetRemoteLocations() const
1784{
1785 std::string s;
1786 if ( ! m_remote_locations.empty())
1787 {
1788 size_t sl = 0;
1789 int nl = 0;
1790 for (std::set<std::string>::iterator i = m_remote_locations.begin(); i != m_remote_locations.end(); ++i, ++nl)
1791 {
1792 sl += i->size();
1793 }
1794 s.reserve(2 + sl + 2*nl + nl - 1 + 1);
1795 s = '[';
1796 int j = 1;
1797 for (std::set<std::string>::iterator i = m_remote_locations.begin(); i != m_remote_locations.end(); ++i, ++j)
1798 {
1799 s += '"'; s += *i; s += '"';
1800 if (j < nl) s += ',';
1801 }
1802 s += ']';
1803 }
1804 else
1805 {
1806 s = "[]";
1807 }
1808 return s;
1809}
1810
1811//==============================================================================
1812//======================= RESPONSE HANDLERS ==============================
1813//==============================================================================
1814
1816{
1817 m_block->m_file->ProcessBlockResponse(m_block, res);
1818 delete this;
1819}
1820
1821//------------------------------------------------------------------------------
1822
1824{
1825 m_mutex.Lock();
1826
1827 int n_left = --m_to_wait;
1828
1829 if (res < 0) {
1830 if (m_errno == 0) m_errno = res; // store first reported error
1831 } else {
1832 m_bytes_read += res;
1833 }
1834
1835 m_mutex.UnLock();
1836
1837 if (n_left == 0)
1838 {
1839 m_file->ProcessDirectReadFinished(m_read_req, m_bytes_read, m_errno);
1840 delete this;
1841 }
1842}
@ kXR_Unsupported
#define TRACE_Debug
#define XrdOssOK
Definition XrdOss.hh:54
#define XRDOSS_mkpath
Definition XrdOss.hh:526
#define TRACE_Error
Definition XrdPfcTrace.hh:7
#define TRACE_Dump
#define TRACEF(act, x)
#define ERRNO_AND_ERRSTR(err_code)
#define TRACEF_INT(act, x)
#define stat(a, b)
Definition XrdPosix.hh:105
#define XRD_TRACE
bool Debug
XrdOucString File
const char * XrdSysE2T(int errcode)
Definition XrdSysE2T.cc:101
#define TRACE(act, x)
Definition XrdTrace.hh:63
virtual int Fstat(struct stat *buf)
Definition XrdOss.hh:164
virtual int Open(const char *path, int Oflag, mode_t Mode, XrdOucEnv &env)
Definition XrdOss.hh:228
virtual int Create(const char *tid, const char *path, mode_t mode, XrdOucEnv &env, int opts=0)=0
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 void Done(int result)=0
virtual int Fcntl(XrdOucCacheOp::Code opc, const std::string &args, std::string &resp)
virtual const char * Path()=0
virtual int pgRead(char *buff, long long offs, int rdlen, std::vector< uint32_t > &csvec, uint64_t opts=0, int *csfix=0)
virtual int ReadV(const XrdOucIOVec *readV, int rnum)
void Put(const char *varname, const char *value)
Definition XrdOucEnv.hh:85
void Done(int result) override
int get_size() const
int get_error() const
int get_n_cksum_errors()
int * ptr_n_cksum_errors()
IO * get_io() const
vCkSum_t & ref_cksum_vec()
long long get_offset() const
vChunkRequest_t m_chunk_reqs
void set_error(int err)
void * get_req_id() const
void set_downloaded()
bool req_cksum_net() const
char * get_buff() const
bool has_cksums() const
long long m_offset
void reset_error_and_set_io(IO *io, void *rid)
int get_req_size() const
Attaches/creates and detaches/deletes cache-io objects for disk based cache.
Definition XrdPfc.hh:185
XrdSysError * GetLog() const
Definition XrdPfc.hh:320
const Configuration & RefConfiguration() const
Reference XrdPfc configuration.
Definition XrdPfc.hh:241
static ResourceMonitor & ResMon()
Definition XrdPfc.cc:139
static Cache & GetInstance()
Singleton access.
Definition XrdPfc.cc:136
XrdSysTrace * GetTrace() const
Definition XrdPfc.hh:321
int UnlinkFile(const std::string &f_name, bool fail_if_open)
Remove cinfo and data files from cache.
Definition XrdPfc.cc:1267
static const Cache & TheOne()
Definition XrdPfc.cc:137
XrdOss * GetOss() const
Definition XrdPfc.hh:306
void Done(int result) override
bool FinalizeSyncBeforeExit()
Returns true if any of blocks need sync. Called from Cache::dec_ref_cnt on zero ref cnt.
const char * lPath() const
Log path.
int ReadV(IO *io, const XrdOucIOVec *readV, int readVnum, ReadReqRH *rh)
Vector read.
void WriteBlockToDisk(Block *b)
float GetPrefetchScore() const
friend class BlockResponseHandler
std::string GetRemoteLocations() const
int Fstat(struct stat &sbuff)
void AddIO(IO *io)
static File * FileOpen(const std::string &path, long long offset, long long fileSize, XrdOucCacheIO *inputIO)
Static constructor that also does Open. Returns null ptr if Open fails.
void RequestSyncOfDetachStats()
Flags that detach stats should be written out in final sync. Called from CacheIO upon Detach.
void BlocksRemovedFromWriteQ(std::list< Block * > &)
Handle removal of a set of blocks from Cache's write queue.
friend class DirectResponseHandler
void Sync()
Sync file cache inf o and output data with disk.
XrdSysTrace * GetTrace() const
int Read(IO *io, char *buff, long long offset, int size, ReadReqRH *rh)
Normal read.
void ioUpdated(IO *io)
Notification from IO that it has been updated (remote open).
long long initiate_emergency_shutdown()
void RemoveIO(IO *io)
void BlockRemovedFromWriteQ(Block *)
Handle removal of a block from Cache's write queue.
XrdSysError * GetLog() const
bool ioActive(IO *io)
Initiate close. Return true if still IO active. Used in XrdPosixXrootd::Close().
Base cache-io class that implements some XrdOucCacheIO abstract methods.
Definition XrdPfcIO.hh:16
bool register_incomplete_read()
Definition XrdPfcIO.hh:87
XrdOucCacheIO * GetInput()
Definition XrdPfcIO.cc:31
bool register_block_error(int res)
Definition XrdPfcIO.hh:90
RAtomic_int m_active_read_reqs
number of active read requests
Definition XrdPfcIO.hh:67
const char * GetLocation()
Definition XrdPfcIO.hh:41
Status of cached file. Can be read from and written into a binary file.
Definition XrdPfcInfo.hh:41
static const char * s_infoExtension
void CrossCheckIfScanIsInProgress(const std::string &lfn, XrdSysCondVar &cond)
int register_file_open(const std::string &filename, time_t open_timestamp, bool existing_file)
void register_file_purge(DirState *target, long long size_in_st_blocks)
void register_file_update_stats(int token_id, const Stats &stats)
void register_file_close(int token_id, time_t close_timestamp, const Stats &full_stats)
Statistics of cache utilisation by a File object.
long long m_StBlocksAdded
number of 512-byte blocks the file has grown by
long long m_BytesBypassed
number of bytes served directly through XrdCl
void AddUp(const Stats &s)
long long BytesReadAndWritten() const
long long m_BytesHit
number of bytes served from disk
std::list< Block * > BlockList_t
constexpr int kReadPending
Definition XrdPfcFile.hh:54
std::vector< ChunkRequest > vChunkRequest_t
std::list< Block * >::iterator BlockList_i
XrdPosixStats Stats
static const int maxRVdsz
Definition XProtocol.hh:724
static const int maxRvecsz
Definition XProtocol.hh:722
long long offset
ReadRequest * m_read_req
Definition XrdPfcFile.hh:98
Contains parameters configurable from the xrootd config file.
Definition XrdPfc.hh:67
long long m_cgi_max_bufferSize
max buffer size allowed in pfc.blocksize
Definition XrdPfc.hh:119
int m_cgi_min_prefetch_max_blocks
min prefetch block count allowed in pfc.prefetch
Definition XrdPfc.hh:120
bool does_cschk_have_missing_bits(CkSumCheck_e cks_on_file) const
Definition XrdPfc.hh:83
bool m_cgi_prefetch_allowed
allow cgi setting of prefetch
Definition XrdPfc.hh:123
CkSumCheck_e get_cs_Chk() const
Definition XrdPfc.hh:76
int m_prefetch_max_blocks
default maximum number of blocks to prefetch per file
Definition XrdPfc.hh:116
bool should_uvkeep_purge(time_t delta) const
Definition XrdPfc.hh:85
std::string m_data_space
oss space for data files
Definition XrdPfc.hh:92
long long m_bufferSize
cache block size, default 128 kB
Definition XrdPfc.hh:111
long long m_cgi_min_bufferSize
min buffer size allowed in pfc.blocksize
Definition XrdPfc.hh:118
std::string m_meta_space
oss space for metadata files (cinfo)
Definition XrdPfc.hh:93
int m_cgi_max_prefetch_max_blocks
max prefetch block count allowed in pfc.prefetch
Definition XrdPfc.hh:121
std::string m_username
username passed to oss plugin
Definition XrdPfc.hh:91
bool m_cgi_blocksize_allowed
allow cgi setting of blocksize
Definition XrdPfc.hh:122
unsigned short m_seq_id
Definition XrdPfcFile.hh:60
void update_error_cond(int ec)
Definition XrdPfcFile.hh:88
bool is_complete() const
Definition XrdPfcFile.hh:90
int return_value() const
Definition XrdPfcFile.hh:91
long long m_bytes_read
Definition XrdPfcFile.hh:75