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