1 /** @file
2
3 Main program file for Cache Tool.
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 #include <iostream>
25 #include <list>
26 #include <memory>
27 #include <vector>
28 #include <map>
29 #include <system_error>
30 #include <fcntl.h>
31 #include <cctype>
32 #include <cstring>
33 #include <vector>
34 #include <unordered_set>
35 #include <ctime>
36 #include <bitset>
37 #include <cinttypes>
38
39 #include "tscore/ink_memory.h"
40 #include "tscore/ink_file.h"
41 #include "tscore/BufferWriter.h"
42 #include "tscore/CryptoHash.h"
43 #include "tscore/ArgParser.h"
44 #include <thread>
45
46 #include "CacheDefs.h"
47 #include "CacheScan.h"
48
49 using ts::Bytes;
50 using ts::Megabytes;
51 using ts::CacheStoreBlocks;
52 using ts::CacheStripeBlocks;
53 using ts::StripeMeta;
54 using ts::CacheStripeDescriptor;
55 using ts::Errata;
56 using ts::CacheDirEntry;
57 using ts::MemSpan;
58 using ts::Doc;
59
60 enum { SILENT = 0, NORMAL, VERBOSE } Verbosity = NORMAL;
61 extern int cache_config_min_average_object_size;
62 extern CacheStoreBlocks Vol_hash_alloc_size;
63 extern int OPEN_RW_FLAG;
64 const Bytes ts::CacheSpan::OFFSET{CacheStoreBlocks{1}};
65 ts::file::path SpanFile;
66 ts::file::path VolumeFile;
67 ts::ArgParser parser;
68
69 Errata err;
70
71 namespace ct
72 {
73 /* --------------------------------------------------------------------------------------- */
74 /// A live volume.
75 /// Volume data based on data from loaded spans.
76 struct Volume {
77 int _idx; ///< Volume index.
78 CacheStoreBlocks _size; ///< Amount of storage allocated.
79 std::vector<Stripe *> _stripes;
80
81 /// Remove all data related to @a span.
82 // void clearSpan(Span *span);
83 /// Remove all allocated space and stripes.
84 void clear();
85 };
86
87 #if 0
88 void
89 Volume::clearSpan(Span* span)
90 {
91 auto spot = std::remove_if(_stripes.begin(), _stripes.end(), [span,this](Stripe* stripe) { return stripe->_span == span ? ( this->_size -= stripe->_len , true ) : false; });
92 _stripes.erase(spot, _stripes.end());
93 }
94 #endif
95
96 void
clear()97 Volume::clear()
98 {
99 _size.assign(0);
100 _stripes.clear();
101 }
102 /* --------------------------------------------------------------------------------------- */
103 /// Data parsed from the volume config file.
104 struct VolumeConfig {
105 Errata load(ts::file::path const &path);
106
107 /// Data direct from the config file.
108 struct Data {
109 int _idx = 0; ///< Volume index.
110 int _percent = 0; ///< Size if specified as a percent.
111 Megabytes _size{0}; ///< Size if specified as an absolute.
112 CacheStripeBlocks _alloc; ///< Allocation size.
113
114 // Methods handy for parsing
115 bool
hasSizect::VolumeConfig::Data116 hasSize() const
117 {
118 return _percent > 0 || _size > 0;
119 }
120 bool
hasIndexct::VolumeConfig::Data121 hasIndex() const
122 {
123 return _idx > 0;
124 }
125 };
126
127 std::vector<Data> _volumes;
128 using iterator = std::vector<Data>::iterator;
129 using const_iterator = std::vector<Data>::const_iterator;
130
131 iterator
beginct::VolumeConfig132 begin()
133 {
134 return _volumes.begin();
135 }
136 iterator
endct::VolumeConfig137 end()
138 {
139 return _volumes.end();
140 }
141 #if 0
142 const_iterator
143 begin() const
144 {
145 return _volumes.begin();
146 }
147 const_iterator
148 end() const
149 {
150 return _volumes.end();
151 }
152 #endif
153 // Errata validatePercentAllocation();
154 void convertToAbsolute(const ts::CacheStripeBlocks &total_span_size);
155 };
156
157 #if 0
158 Errata
159 VolumeConfig::validatePercentAllocation()
160 {
161 Errata zret;
162 int n = 0;
163 for (auto &vol : _volumes)
164 n += vol._percent;
165 if (n > 100)
166 zret.push(0, 10, "Volume percent allocation ", n, " is more than 100%");
167 return zret;
168 }
169 #endif
170
171 void
convertToAbsolute(const ts::CacheStripeBlocks & n)172 VolumeConfig::convertToAbsolute(const ts::CacheStripeBlocks &n)
173 {
174 for (auto &vol : _volumes) {
175 if (vol._percent) {
176 vol._alloc.assign((n.count() * vol._percent + 99) / 100);
177 } else {
178 vol._alloc = round_up(vol._size);
179 }
180 }
181 }
182 /* --------------------------------------------------------------------------------------- */
183 struct Cache {
184 ~Cache();
185
186 Errata loadSpan(ts::file::path const &path);
187 Errata loadSpanConfig(ts::file::path const &path);
188 Errata loadSpanDirect(ts::file::path const &path, int vol_idx = -1, const Bytes &size = Bytes(-1));
189 Errata loadURLs(ts::file::path const &path);
190
191 Errata allocStripe(Span *span, int vol_idx, const CacheStripeBlocks &len);
192
193 /// Change the @a span to have a single, unused stripe occupying the entire @a span.
194 // void clearSpan(Span *span);
195 /// Clear all allocated space.
196 void clearAllocation();
197
198 enum class SpanDumpDepth { SPAN, STRIPE, DIRECTORY };
199 void dumpSpans(SpanDumpDepth depth);
200 void dumpVolumes();
201 void build_stripe_hash_table();
202 Stripe *key_to_stripe(CryptoHash *key, const char *hostname, int host_len);
203 // ts::CacheStripeBlocks calcTotalSpanPhysicalSize();
204 ts::CacheStripeBlocks calcTotalSpanConfiguredSize();
205
206 std::list<Span *> _spans;
207 std::map<int, Volume> _volumes;
208 std::vector<Stripe *> globalVec_stripe;
209 std::unordered_set<ts::CacheURL *> URLset;
210 unsigned short *stripes_hash_table;
211 };
212
213 Errata
allocStripe(Span * span,int vol_idx,const CacheStripeBlocks & len)214 Cache::allocStripe(Span *span, int vol_idx, const CacheStripeBlocks &len)
215 {
216 auto rv = span->allocStripe(vol_idx, len);
217 std::cout << span->_path.string() << ":" << vol_idx << std::endl;
218 if (rv.isOK()) {
219 _volumes[vol_idx]._stripes.push_back(rv);
220 }
221 return rv.errata();
222 }
223
224 #if 0
225 void
226 Cache::clearSpan(Span* span)
227 {
228 for ( auto& item : _volumes ) item.second.clearSpan(span);
229 span->clear();
230 }
231 #endif
232
233 void
clearAllocation()234 Cache::clearAllocation()
235 {
236 for (auto span : _spans) {
237 span->clear();
238 }
239 for (auto &item : _volumes) {
240 item.second.clear();
241 }
242 }
243 /* --------------------------------------------------------------------------------------- */
244 /// Temporary structure used for doing allocation computations.
245 class VolumeAllocator
246 {
247 /// Working struct that tracks allocation information.
248 struct V {
249 VolumeConfig::Data const &_config; ///< Configuration instance.
250 CacheStripeBlocks _size; ///< Current actual size.
251 int64_t _deficit; ///< fractional deficit
252 int64_t _shares; ///< relative amount of free space to allocate
253
Vct::VolumeAllocator::V254 V(VolumeConfig::Data const &config, const CacheStripeBlocks &size, int64_t deficit = 0, int64_t shares = 0)
255 : _config(config), _size(size), _deficit(deficit), _shares(shares)
256 {
257 }
258 V(const V &v) = default;
259
260 V &
operator =ct::VolumeAllocator::V261 operator=(V const &that)
262 {
263 new (this) V(that._config, that._size, that._deficit, that._shares);
264 return *this;
265 }
266 };
267
268 using AV = std::vector<V>;
269 AV _av; ///< Working vector of volume data.
270
271 Cache _cache; ///< Current state.
272 VolumeConfig _vols; ///< Configuration state.
273
274 public:
275 VolumeAllocator();
276
277 Errata load(ts::file::path const &spanFile, ts::file::path const &volumeFile);
278 Errata fillEmptySpans();
279 Errata fillAllSpans();
280 Errata allocateSpan(ts::file::path const &spanFile);
281 void dumpVolumes();
282
283 protected:
284 /// Update the allocation for a span.
285 Errata allocateFor(Span &span);
286 };
287
288 VolumeAllocator::VolumeAllocator() = default;
289
290 Errata
load(ts::file::path const & spanFile,ts::file::path const & volumeFile)291 VolumeAllocator::load(ts::file::path const &spanFile, ts::file::path const &volumeFile)
292 {
293 Errata zret;
294
295 if (volumeFile.empty()) {
296 zret.push(0, 9, "Volume config file not set");
297 }
298 if (spanFile.empty()) {
299 zret.push(0, 9, "Span file not set");
300 }
301
302 if (zret) {
303 zret = _vols.load(volumeFile);
304 if (zret) {
305 zret = _cache.loadSpan(spanFile);
306 if (zret) {
307 CacheStripeBlocks total = _cache.calcTotalSpanConfiguredSize();
308 _vols.convertToAbsolute(total);
309 for (auto &vol : _vols) {
310 CacheStripeBlocks size(0);
311 auto spot = _cache._volumes.find(vol._idx);
312 if (spot != _cache._volumes.end()) {
313 size = round_down(spot->second._size);
314 }
315 _av.push_back({vol, size, 0, 0});
316 }
317 }
318 }
319 }
320 return zret;
321 }
322
323 void
dumpVolumes()324 VolumeAllocator::dumpVolumes()
325 {
326 _cache.dumpVolumes();
327 }
328
329 Errata
fillEmptySpans()330 VolumeAllocator::fillEmptySpans()
331 {
332 Errata zret;
333 // Walk the spans, skipping ones that are not empty.
334 for (auto span : _cache._spans) {
335 if (span->isEmpty()) {
336 this->allocateFor(*span);
337 }
338 }
339 return zret;
340 }
341
342 Errata
allocateSpan(ts::file::path const & input_file_path)343 VolumeAllocator::allocateSpan(ts::file::path const &input_file_path)
344 {
345 Errata zret;
346 for (auto span : _cache._spans) {
347 if (span->_path.view() == input_file_path.view()) {
348 std::cout << "===============================" << std::endl;
349 if (span->_header) {
350 zret.push(0, 1, "Disk already initialized with valid header");
351 } else {
352 this->allocateFor(*span);
353 span->updateHeader();
354 for (auto &strp : span->_stripes) {
355 strp->updateHeaderFooter();
356 }
357 }
358 }
359 }
360 for (auto &_v : _av) {
361 std::cout << _v._size << std::endl;
362 }
363 return zret;
364 }
365
366 Errata
fillAllSpans()367 VolumeAllocator::fillAllSpans()
368 {
369 Errata zret;
370 // clear all current volume allocations.
371 for (auto &v : _av) {
372 v._size.assign(0);
373 }
374 // Allocate for each span, clearing as it goes.
375 _cache.clearAllocation();
376 for (auto span : _cache._spans) {
377 this->allocateFor(*span);
378 }
379 return zret;
380 }
381
382 Errata
allocateFor(Span & span)383 VolumeAllocator::allocateFor(Span &span)
384 {
385 Errata zret;
386
387 /// Scaling factor for shares, effectively the accuracy.
388 static const int64_t SCALE = 1000;
389 int64_t total_shares = 0;
390
391 if (Verbosity >= NORMAL) {
392 std::cout << "Allocating " << CacheStripeBlocks(round_down(span._len)).count() << " stripe blocks from span "
393 << span._path.string() << std::endl;
394 }
395
396 // Walk the volumes and get the relative allocations.
397 for (auto &v : _av) {
398 auto delta = v._config._alloc - v._size;
399 if (delta > 0) {
400 v._deficit = (delta.count() * SCALE) / v._config._alloc.count();
401 v._shares = delta.count() * v._deficit;
402 total_shares += v._shares;
403 } else {
404 v._shares = 0;
405 }
406 }
407 assert(total_shares != 0);
408 // Now allocate blocks.
409 CacheStripeBlocks span_blocks(round_down(span._free_space));
410 CacheStripeBlocks span_used{0};
411
412 // sort by deficit so least relatively full volumes go first.
413 std::sort(_av.begin(), _av.end(), [](V const &lhs, V const &rhs) { return lhs._deficit > rhs._deficit; });
414 for (auto &v : _av) {
415 if (v._shares) {
416 CacheStripeBlocks n{(((span_blocks - span_used).count() * v._shares) + total_shares - 1) / total_shares};
417 CacheStripeBlocks delta{v._config._alloc - v._size};
418 // Not sure why this is needed. But a large and empty volume can dominate the shares
419 // enough to get more than it actually needs if the other volume are relative small or full.
420 // I need to do more math to see if the weighting can be adjusted to not have this happen.
421 n = std::min(n, delta);
422 v._size += n;
423 span_used += n;
424 total_shares -= v._shares;
425 Errata z = _cache.allocStripe(&span, v._config._idx, round_up(n));
426 if (Verbosity >= NORMAL) {
427 std::cout << " " << n << " to volume " << v._config._idx << std::endl;
428 }
429 if (!z) {
430 std::cout << z;
431 }
432 }
433 }
434 if (Verbosity >= NORMAL) {
435 std::cout << " Total " << span_used << std::endl;
436 }
437 if (OPEN_RW_FLAG) {
438 if (Verbosity >= NORMAL) {
439 std::cout << " Updating Header ... ";
440 }
441 zret = span.updateHeader();
442 }
443 _cache.dumpVolumes(); // debug
444 if (Verbosity >= NORMAL) {
445 if (zret) {
446 std::cout << " Done" << std::endl;
447 } else {
448 std::cout << " Error" << std::endl << zret;
449 }
450 }
451
452 return zret;
453 }
454 /* --------------------------------------------------------------------------------------- */
455 Errata
loadSpan(ts::file::path const & path)456 Cache::loadSpan(ts::file::path const &path)
457 {
458 Errata zret;
459 std::error_code ec;
460 auto fs = ts::file::status(path, ec);
461
462 if (path.empty()) {
463 zret = Errata::Message(0, EINVAL, "A span file specified by --spans is required");
464 } else if (!ts::file::is_readable(path)) {
465 zret = Errata::Message(0, EPERM, '\'', path.string(), "' is not readable.");
466 } else if (ts::file::is_regular_file(fs)) {
467 zret = this->loadSpanConfig(path);
468 } else {
469 zret = this->loadSpanDirect(path);
470 }
471 return zret;
472 }
473
474 Errata
loadSpanDirect(ts::file::path const & path,int vol_idx,const Bytes & size)475 Cache::loadSpanDirect(ts::file::path const &path, int vol_idx, const Bytes &size)
476 {
477 Errata zret;
478 std::unique_ptr<Span> span(new Span(path));
479 zret = span->load();
480 if (zret) {
481 if (span->_header) {
482 int nspb = span->_header->num_diskvol_blks;
483 for (auto i = 0; i < nspb; ++i) {
484 ts::CacheStripeDescriptor &raw = span->_header->stripes[i];
485 Stripe *stripe = new Stripe(span.get(), raw.offset, raw.len);
486 stripe->_idx = i;
487 if (raw.free == 0) {
488 stripe->_vol_idx = raw.vol_idx;
489 stripe->_type = raw.type;
490 _volumes[stripe->_vol_idx]._stripes.push_back(stripe);
491 _volumes[stripe->_vol_idx]._size += stripe->_len;
492 stripe->vol_init_data();
493 } else {
494 span->_free_space += stripe->_len;
495 }
496 span->_stripes.push_back(stripe);
497 globalVec_stripe.push_back(stripe);
498 }
499 span->_vol_idx = vol_idx;
500 } else {
501 span->clear();
502 }
503 _spans.push_back(span.release());
504 }
505 return zret;
506 }
507
508 Errata
loadSpanConfig(ts::file::path const & path)509 Cache::loadSpanConfig(ts::file::path const &path)
510 {
511 static const ts::TextView TAG_ID("id");
512 static const ts::TextView TAG_VOL("volume");
513
514 Errata zret;
515 std::error_code ec;
516 std::string load_content = ts::file::load(path, ec);
517 if (ec.value() == 0) {
518 ts::TextView content(load_content);
519 while (content) {
520 ts::TextView line = content.take_prefix_at('\n');
521 line.ltrim_if(&isspace);
522 if (line.empty() || '#' == *line) {
523 continue;
524 }
525 ts::TextView localpath = line.take_prefix_if(&isspace);
526 if (localpath) {
527 // After this the line is [size] [id=string] [volume=#]
528 while (line) {
529 ts::TextView value(line.take_prefix_if(&isspace));
530 if (value) {
531 ts::TextView tag(value.take_prefix_at('='));
532 if (!tag) { // must be the size
533 } else if (0 == strcasecmp(tag, TAG_ID)) {
534 } else if (0 == strcasecmp(tag, TAG_VOL)) {
535 ts::TextView text;
536 auto n = ts::svtoi(value, &text);
537 if (text == value && 0 < n && n < 256) {
538 } else {
539 zret.push(0, 0, "Invalid volume index '", value, "'");
540 }
541 }
542 }
543 }
544 zret = this->loadSpan(ts::file::path(localpath));
545 }
546 }
547 } else {
548 zret = Errata::Message(0, EBADF, "Unable to load ", path.string());
549 }
550 return zret;
551 }
552
553 Errata
loadURLs(ts::file::path const & path)554 Cache::loadURLs(ts::file::path const &path)
555 {
556 static const ts::TextView TAG_VOL("url");
557 ts::URLparser loadURLparser;
558 Errata zret;
559
560 std::error_code ec;
561 std::string load_content = ts::file::load(path, ec);
562 if (ec.value() == 0) {
563 ts::TextView content(load_content);
564 while (!content.empty()) {
565 ts::TextView blob = content.take_prefix_at('\n');
566
567 ts::TextView tag(blob.take_prefix_at('='));
568 if (tag.empty()) {
569 } else if (0 == strcasecmp(tag, TAG_VOL)) {
570 std::string url;
571 url.assign(blob.data(), blob.size());
572 int port_ptr = -1, port_len = -1;
573 int port = loadURLparser.getPort(url, port_ptr, port_len);
574 if (port_ptr >= 0 && port_len > 0) {
575 url.erase(port_ptr, port_len + 1); // get rid of :PORT
576 }
577 std::cout << "port # " << port << ":" << port_ptr << ":" << port_len << ":" << url << std::endl;
578 ts::CacheURL *curl = new ts::CacheURL(url, port);
579 URLset.emplace(curl);
580 }
581 }
582 } else {
583 zret = Errata::Message(0, EBADF, "Unable to load ", path.string());
584 }
585 return zret;
586 }
587
588 void
dumpSpans(SpanDumpDepth depth)589 Cache::dumpSpans(SpanDumpDepth depth)
590 {
591 if (depth >= SpanDumpDepth::SPAN) {
592 for (auto span : _spans) {
593 if (nullptr == span->_header) {
594 std::cout << "Span: " << span->_path.string() << " is uninitialized" << std::endl;
595 } else {
596 std::cout << "\n----------------------------------\n"
597 << "Span: " << span->_path.string() << "\n----------------------------------\n"
598 << "#Magic: " << span->_header->magic << " #Volumes: " << span->_header->num_volumes
599 << " #in use: " << span->_header->num_used << " #free: " << span->_header->num_free
600 << " #stripes: " << span->_header->num_diskvol_blks << " Len(bytes): " << span->_header->num_blocks.value()
601 << std::endl;
602
603 for (auto stripe : span->_stripes) {
604 std::cout << "\n>>>>>>>>> Stripe " << static_cast<int>(stripe->_idx) << " @ " << stripe->_start
605 << " len=" << stripe->_len.count() << " blocks "
606 << " vol=" << static_cast<int>(stripe->_vol_idx) << " type=" << static_cast<int>(stripe->_type) << " "
607 << (stripe->isFree() ? "free" : "in-use") << std::endl;
608
609 std::cout << " " << stripe->_segments << " segments with " << stripe->_buckets << " buckets per segment for "
610 << stripe->_buckets * stripe->_segments * ts::ENTRIES_PER_BUCKET << " total directory entries taking "
611 << stripe->_buckets * stripe->_segments * sizeof(CacheDirEntry) * ts::ENTRIES_PER_BUCKET
612 // << " out of " << (delta-header_len).units() << " bytes."
613 << std::endl;
614 if (depth >= SpanDumpDepth::STRIPE) {
615 Errata r = stripe->loadMeta();
616 if (r) {
617 // print Meta[A][HEAD]
618 std::string MetaCopy[2] = {"A", "B"};
619 std::string MetaType[2] = {"HEAD", "FOOT"};
620 for (int i = 0; i < 2; i++) {
621 for (int j = 0; j < 2; j++) {
622 std::cout << "\n" << MetaCopy[i] << ":" << MetaType[j] << "\n" << std::endl;
623 std::cout << " Magic:" << stripe->_meta[i][j].magic << "\n version: major: " << stripe->_meta[i][j].version._major
624 << "\n version: minor: " << stripe->_meta[i][j].version._minor
625 << "\n create_time: " << stripe->_meta[i][j].create_time
626 << "\n write_pos: " << stripe->_meta[i][j].write_pos
627 << "\n last_write_pos: " << stripe->_meta[i][j].last_write_pos
628 << "\n agg_pos: " << stripe->_meta[i][j].agg_pos << "\n generation: " << stripe->_meta[i][j].generation
629 << "\n phase: " << stripe->_meta[i][j].phase << "\n cycle: " << stripe->_meta[i][j].cycle
630 << "\n sync_serial: " << stripe->_meta[i][j].sync_serial
631 << "\n write_serial: " << stripe->_meta[i][j].write_serial << "\n dirty: " << stripe->_meta[i][j].dirty
632 << "\n sector_size: " << stripe->_meta[i][j].sector_size << std::endl;
633 }
634 }
635 if (!stripe->validate_sync_serial()) {
636 std::cout << "WARNING:::::Validity check failed for sync_serials" << std::endl;
637 }
638 stripe->_directory.clear();
639 } else {
640 std::cout << r;
641 }
642 }
643 }
644 }
645 }
646 }
647 }
648
649 void
dumpVolumes()650 Cache::dumpVolumes()
651 {
652 for (auto const &elt : _volumes) {
653 size_t size = 0;
654 for (auto const &r : elt.second._stripes) {
655 size += r->_len;
656 }
657
658 std::cout << "Volume " << elt.first << " has " << elt.second._stripes.size() << " stripes and " << size << " bytes"
659 << std::endl;
660 }
661 }
662
663 ts::CacheStripeBlocks
calcTotalSpanConfiguredSize()664 Cache::calcTotalSpanConfiguredSize()
665 {
666 ts::CacheStripeBlocks zret(0);
667
668 for (auto span : _spans) {
669 zret += round_down(span->_len);
670 }
671 return zret;
672 }
673
674 #if 0
675 ts::CacheStripeBlocks
676 Cache::calcTotalSpanPhysicalSize()
677 {
678 ts::CacheStripeBlocks zret(0);
679
680 for (auto span : _spans) {
681 // This is broken, physical_size doesn't work for devices, need to fix that.
682 zret += round_down(span->_path.physical_size());
683 }
684 return zret;
685 }
686 #endif
687
~Cache()688 Cache::~Cache()
689 {
690 for (auto *span : _spans) {
691 delete span;
692 }
693 }
694
695 Errata
load()696 Span::load()
697 {
698 Errata zret;
699 std::error_code ec;
700 auto fs = ts::file::status(_path, ec);
701
702 if (!ts::file::is_readable(_path)) {
703 zret = Errata::Message(0, EPERM, _path.string(), " is not readable.");
704 } else if (ts::file::is_char_device(fs) || ts::file::is_block_device(fs)) {
705 zret = this->loadDevice();
706 } else if (ts::file::is_dir(fs)) {
707 zret.push(0, 1, "Directory support not yet available");
708 } else {
709 zret.push(0, EBADF, _path.string(), " is not a valid file type");
710 }
711 return zret;
712 }
713
714 Errata
loadDevice()715 Span::loadDevice()
716 {
717 Errata zret;
718 int flags;
719
720 flags = OPEN_RW_FLAG
721 #if defined(O_DIRECT)
722 | O_DIRECT
723 #endif
724 #if defined(O_DSYNC)
725 | O_DSYNC
726 #endif
727 ;
728
729 ats_scoped_fd fd(!_path.empty() ? ::open(_path.c_str(), flags) : ats_scoped_fd());
730
731 if (fd.isValid()) {
732 if (ink_file_get_geometry(fd, _geometry)) {
733 off_t offset = ts::CacheSpan::OFFSET;
734 CacheStoreBlocks span_hdr_size(1); // default.
735 static const ssize_t BUFF_SIZE = CacheStoreBlocks::SCALE; // match default span_hdr_size
736 alignas(512) char buff[BUFF_SIZE];
737 ssize_t n = pread(fd, buff, BUFF_SIZE, offset);
738 if (n >= BUFF_SIZE) {
739 ts::SpanHeader &span_hdr = reinterpret_cast<ts::SpanHeader &>(buff);
740 _base = round_up(offset);
741 // See if it looks valid
742 if (span_hdr.magic == ts::SpanHeader::MAGIC && span_hdr.num_diskvol_blks == span_hdr.num_used + span_hdr.num_free) {
743 int nspb = span_hdr.num_diskvol_blks;
744 span_hdr_size = round_up(sizeof(ts::SpanHeader) + (nspb - 1) * sizeof(ts::CacheStripeDescriptor));
745 _header.reset(new (malloc(span_hdr_size)) ts::SpanHeader);
746 if (span_hdr_size <= BUFF_SIZE) {
747 memcpy(static_cast<void *>(_header.get()), buff, span_hdr_size);
748 } else {
749 // TODO - check the pread return
750 ssize_t n = pread(fd, _header.get(), span_hdr_size, offset);
751 if (n < span_hdr_size) {
752 std::cout << "Failed to read the Span Header" << std::endl;
753 }
754 }
755 _len = _header->num_blocks;
756 } else {
757 zret = Errata::Message(0, 0, _path.string(), " header is uninitialized or invalid");
758 std::cout << "Span: " << _path.string() << " header is uninitialized or invalid" << std::endl;
759 _len = round_down(_geometry.totalsz) - _base;
760 }
761 // valid FD means the device is accessible and has enough storage to be configured.
762 _fd = fd.release();
763 _offset = _base + span_hdr_size;
764 } else {
765 zret = Errata::Message(0, errno, "Failed to read from ", _path.string(), '[', errno, ':', strerror(errno), ']');
766 }
767 } else {
768 zret = Errata::Message(0, 23, "Unable to get device geometry for ", _path.string());
769 }
770 } else {
771 zret = Errata::Message(0, errno, "Unable to open ", _path.string());
772 }
773 return zret;
774 }
775
776 ts::Rv<Stripe *>
allocStripe(int vol_idx,const CacheStripeBlocks & len)777 Span::allocStripe(int vol_idx, const CacheStripeBlocks &len)
778 {
779 for (auto spot = _stripes.begin(), limit = _stripes.end(); spot != limit; ++spot) {
780 Stripe *stripe = *spot;
781 if (stripe->isFree()) {
782 if (len < stripe->_len) {
783 // If the remains would be less than a stripe block, just take it all.
784 if (stripe->_len <= (len + CacheStripeBlocks(1))) {
785 stripe->_vol_idx = vol_idx;
786 stripe->_type = 1;
787 return stripe;
788 } else {
789 Stripe *ns = new Stripe(this, stripe->_start, len);
790 stripe->_start += len;
791 stripe->_len -= len;
792 ns->_vol_idx = vol_idx;
793 ns->_type = 1;
794 _stripes.insert(spot, ns);
795 return ns;
796 }
797 }
798 }
799 }
800 return ts::Rv<Stripe *>(nullptr,
801 Errata::Message(0, 15, "Failed to allocate stripe of size ", len, " - no free block large enough"));
802 }
803
804 bool
isEmpty() const805 Span::isEmpty() const
806 {
807 return std::all_of(_stripes.begin(), _stripes.end(), [](Stripe *s) { return s->_vol_idx == 0; });
808 }
809
810 Errata
clear()811 Span::clear()
812 {
813 Stripe *stripe;
814 std::for_each(_stripes.begin(), _stripes.end(), [](Stripe *s) { delete s; });
815 _stripes.clear();
816
817 // Gah, due to lack of anything better, TS depends on the number of usable blocks to be consistent
818 // with internal calculations so have to match that here. Yay.
819 CacheStoreBlocks eff = _len - _base; // starting # of usable blocks.
820 // The maximum number of volumes that can store stored, accounting for the space used to store the descriptors.
821 int n = (eff - sizeof(ts::SpanHeader)) / (CacheStripeBlocks::SCALE + sizeof(CacheStripeDescriptor));
822 _offset = _base + round_up(sizeof(ts::SpanHeader) + (n - 1) * sizeof(CacheStripeDescriptor));
823 stripe = new Stripe(this, _offset, _len - _offset);
824 stripe->vol_init_data();
825 stripe->InitializeMeta();
826 _stripes.push_back(stripe);
827 _free_space = stripe->_len;
828
829 return Errata();
830 }
831
832 Errata
updateHeader()833 Span::updateHeader()
834 {
835 Errata zret;
836 int n = _stripes.size();
837 CacheStripeDescriptor *sd;
838 CacheStoreBlocks hdr_size = round_up(sizeof(ts::SpanHeader) + (n - 1) * sizeof(ts::CacheStripeDescriptor));
839 void *raw = ats_memalign(512, hdr_size);
840 ts::SpanHeader *hdr = static_cast<ts::SpanHeader *>(raw);
841 std::bitset<ts::MAX_VOLUME_IDX + 1> volume_mask;
842
843 hdr->magic = ts::SpanHeader::MAGIC;
844 hdr->num_free = 0;
845 hdr->num_used = 0;
846 hdr->num_diskvol_blks = n;
847 hdr->num_blocks = _len;
848
849 sd = hdr->stripes;
850 for (auto stripe : _stripes) {
851 sd->offset = stripe->_start;
852 sd->len = stripe->_len;
853 sd->vol_idx = stripe->_vol_idx;
854 sd->type = stripe->_type;
855 volume_mask[sd->vol_idx] = true;
856 if (sd->vol_idx == 0) {
857 sd->free = true;
858 ++(hdr->num_free);
859 } else {
860 sd->free = false;
861 ++(hdr->num_used);
862 }
863
864 ++sd;
865 }
866 volume_mask[0] = false; // don't include free stripes in distinct volume count.
867 hdr->num_volumes = volume_mask.count();
868 _header.reset(hdr);
869 if (OPEN_RW_FLAG) {
870 ssize_t r = pwrite(_fd, hdr, hdr_size, ts::CacheSpan::OFFSET);
871 if (r < ts::CacheSpan::OFFSET) {
872 zret.push(0, errno, "Failed to update span - ", strerror(errno));
873 }
874 } else {
875 std::cout << "Writing not enabled, no updates performed" << std::endl;
876 }
877 return zret;
878 }
879
880 void
clearPermanently()881 Span::clearPermanently()
882 {
883 if (OPEN_RW_FLAG) {
884 alignas(512) static char zero[CacheStoreBlocks::SCALE]; // should be all zero, it's static.
885 std::cout << "Clearing " << _path.string() << " permanently on disk ";
886 ssize_t n = pwrite(_fd, zero, sizeof(zero), ts::CacheSpan::OFFSET);
887 if (n == sizeof(zero)) {
888 std::cout << "done";
889 } else {
890 const char *text = strerror(errno);
891 std::cout << "failed";
892 if (n >= 0) {
893 std::cout << " - " << n << " of " << sizeof(zero) << " bytes written";
894 }
895 std::cout << " - " << text;
896 }
897
898 std::cout << std::endl;
899 // clear the stripes as well
900 for (auto *strp : _stripes) {
901 strp->loadMeta();
902 std::cout << "Clearing stripe @" << strp->_start << " of length: " << strp->_len << std::endl;
903 strp->clear();
904 }
905 } else {
906 std::cout << "Clearing " << _path.string() << " not performed, write not enabled" << std::endl;
907 }
908 }
909
910 // explicit pair for random table in build_vol_hash_table
911 struct rtable_pair {
912 unsigned int rval; ///< relative value, used to sort.
913 unsigned int idx; ///< volume mapping table index.
914 };
915
916 // comparison operator for random table in build_vol_hash_table
917 // sorts based on the randomly assigned rval
918 static int
cmprtable(const void * aa,const void * bb)919 cmprtable(const void *aa, const void *bb)
920 {
921 rtable_pair *a = (rtable_pair *)aa;
922 rtable_pair *b = (rtable_pair *)bb;
923 if (a->rval < b->rval) {
924 return -1;
925 }
926 if (a->rval > b->rval) {
927 return 1;
928 }
929 return 0;
930 }
931
932 unsigned int
next_rand(unsigned int * p)933 next_rand(unsigned int *p)
934 {
935 unsigned int seed = *p;
936 seed = 1103515145 * seed + 12345;
937 *p = seed;
938 return seed;
939 }
940
941 void
build_stripe_hash_table()942 Cache::build_stripe_hash_table()
943 {
944 int num_stripes = globalVec_stripe.size();
945 CacheStoreBlocks total;
946 unsigned int *forvol = static_cast<unsigned int *>(ats_malloc(sizeof(unsigned int) * num_stripes));
947 unsigned int *gotvol = static_cast<unsigned int *>(ats_malloc(sizeof(unsigned int) * num_stripes));
948 unsigned int *rnd = static_cast<unsigned int *>(ats_malloc(sizeof(unsigned int) * num_stripes));
949 unsigned short *ttable = static_cast<unsigned short *>(ats_malloc(sizeof(unsigned short) * VOL_HASH_TABLE_SIZE));
950 unsigned int *rtable_entries = static_cast<unsigned int *>(ats_malloc(sizeof(unsigned int) * num_stripes));
951 unsigned int rtable_size = 0;
952 int i = 0;
953 uint64_t used = 0;
954
955 // estimate allocation
956 for (auto &elt : globalVec_stripe) {
957 // printf("stripe length %" PRId64 "\n", elt->_len.count());
958 rtable_entries[i] = static_cast<int64_t>(elt->_len) / Vol_hash_alloc_size;
959 rtable_size += rtable_entries[i];
960 uint64_t x = elt->hash_id.fold();
961 // seed random number generator
962 rnd[i] = static_cast<unsigned int>(x);
963 total += elt->_len;
964 i++;
965 }
966 i = 0;
967 for (auto &elt : globalVec_stripe) {
968 forvol[i] = total ? static_cast<int64_t>(VOL_HASH_TABLE_SIZE * elt->_len) / total : 0;
969 used += forvol[i];
970 gotvol[i] = 0;
971 i++;
972 }
973
974 // spread around the excess
975 int extra = VOL_HASH_TABLE_SIZE - used;
976 for (int i = 0; i < extra; i++) {
977 forvol[i % num_stripes]++;
978 }
979
980 // initialize table to "empty"
981 for (int i = 0; i < VOL_HASH_TABLE_SIZE; i++) {
982 ttable[i] = VOL_HASH_EMPTY;
983 }
984
985 // generate random numbers proportional to allocation
986 rtable_pair *rtable = static_cast<rtable_pair *>(ats_malloc(sizeof(rtable_pair) * rtable_size));
987 int rindex = 0;
988 for (int i = 0; i < num_stripes; i++) {
989 for (int j = 0; j < static_cast<int>(rtable_entries[i]); j++) {
990 rtable[rindex].rval = next_rand(&rnd[i]);
991 rtable[rindex].idx = i;
992 rindex++;
993 }
994 }
995 assert(rindex == (int)rtable_size);
996 // sort (rand #, vol $ pairs)
997 qsort(rtable, rtable_size, sizeof(rtable_pair), cmprtable);
998 unsigned int width = (1LL << 32) / VOL_HASH_TABLE_SIZE;
999 unsigned int pos; // target position to allocate
1000 // select vol with closest random number for each bucket
1001 i = 0; // index moving through the random numbers
1002 for (int j = 0; j < VOL_HASH_TABLE_SIZE; j++) {
1003 pos = width / 2 + j * width; // position to select closest to
1004 while (pos > rtable[i].rval && i < static_cast<int>(rtable_size) - 1) {
1005 i++;
1006 }
1007 ttable[j] = rtable[i].idx;
1008 gotvol[rtable[i].idx]++;
1009 }
1010 for (int i = 0; i < num_stripes; i++) {
1011 printf("build_vol_hash_table index %d mapped to %d requested %d got %d\n", i, i, forvol[i], gotvol[i]);
1012 }
1013 stripes_hash_table = ttable;
1014
1015 ats_free(forvol);
1016 ats_free(gotvol);
1017 ats_free(rnd);
1018 ats_free(rtable_entries);
1019 ats_free(rtable);
1020 }
1021
1022 Stripe *
key_to_stripe(CryptoHash * key,const char * hostname,int host_len)1023 Cache::key_to_stripe(CryptoHash *key, const char *hostname, int host_len)
1024 {
1025 uint32_t h = (key->slice32(2) >> DIR_TAG_WIDTH) % VOL_HASH_TABLE_SIZE;
1026 return globalVec_stripe[stripes_hash_table[h]];
1027 }
1028
1029 /* --------------------------------------------------------------------------------------- */
1030 Errata
load(ts::file::path const & path)1031 VolumeConfig::load(ts::file::path const &path)
1032 {
1033 static const ts::TextView TAG_SIZE("size");
1034 static const ts::TextView TAG_VOL("volume");
1035
1036 Errata zret;
1037
1038 int ln = 0;
1039
1040 std::error_code ec;
1041 std::string load_content = ts::file::load(path, ec);
1042 if (ec.value() == 0) {
1043 ts::TextView content(load_content);
1044 while (content) {
1045 Data v;
1046
1047 ++ln;
1048 ts::TextView line = content.take_prefix_at('\n');
1049 line.ltrim_if(&isspace);
1050 if (line.empty() || '#' == *line) {
1051 continue;
1052 }
1053
1054 while (line) {
1055 ts::TextView value(line.take_prefix_if(&isspace));
1056 ts::TextView tag(value.take_prefix_at('='));
1057 if (tag.empty()) {
1058 zret.push(0, 1, "Line ", ln, " is invalid");
1059 } else if (0 == strcasecmp(tag, TAG_SIZE)) {
1060 if (v.hasSize()) {
1061 zret.push(0, 5, "Line ", ln, " has field ", TAG_SIZE, " more than once");
1062 } else {
1063 ts::TextView text;
1064 auto n = ts::svtoi(value, &text);
1065 if (text) {
1066 ts::TextView percent(text.data_end(), value.data_end()); // clip parsed number.
1067 if (percent.empty()) {
1068 v._size = CacheStripeBlocks(round_up(Megabytes(n)));
1069 if (v._size.count() != n) {
1070 zret.push(0, 0, "Line ", ln, " size ", n, " was rounded up to ", v._size);
1071 }
1072 } else if ('%' == *percent && percent.size() == 1) {
1073 v._percent = n;
1074 } else {
1075 zret.push(0, 3, "Line ", ln, " has invalid value '", value, "' for ", TAG_SIZE, " field");
1076 }
1077 } else {
1078 zret.push(0, 2, "Line ", ln, " has invalid value '", value, "' for ", TAG_SIZE, " field");
1079 }
1080 }
1081 } else if (0 == strcasecmp(tag, TAG_VOL)) {
1082 if (v.hasIndex()) {
1083 zret.push(0, 6, "Line ", ln, " has field ", TAG_VOL, " more than once");
1084 } else {
1085 ts::TextView text;
1086 auto n = ts::svtoi(value, &text);
1087 if (text == value) {
1088 v._idx = n;
1089 } else {
1090 zret.push(0, 4, "Line ", ln, " has invalid value '", value, "' for ", TAG_VOL, " field");
1091 }
1092 }
1093 }
1094 }
1095 if (v.hasSize() && v.hasIndex()) {
1096 _volumes.push_back(std::move(v));
1097 } else {
1098 if (!v.hasSize()) {
1099 zret.push(0, 7, "Line ", ln, " does not have the required field ", TAG_SIZE);
1100 }
1101 if (!v.hasIndex()) {
1102 zret.push(0, 8, "Line ", ln, " does not have the required field ", TAG_VOL);
1103 }
1104 }
1105 }
1106 } else {
1107 zret = Errata::Message(0, EBADF, "Unable to load ", path.string());
1108 }
1109 return zret;
1110 }
1111 } // namespace ct
1112
1113 using namespace ct;
1114 void
List_Stripes(Cache::SpanDumpDepth depth)1115 List_Stripes(Cache::SpanDumpDepth depth)
1116 {
1117 Cache cache;
1118
1119 if ((err = cache.loadSpan(SpanFile))) {
1120 cache.dumpSpans(depth);
1121 cache.dumpVolumes();
1122 }
1123 }
1124
1125 void
Cmd_Allocate_Empty_Spans()1126 Cmd_Allocate_Empty_Spans()
1127 {
1128 VolumeAllocator va;
1129
1130 // OPEN_RW_FLAG = O_RDWR;
1131 if ((err = va.load(SpanFile, VolumeFile))) {
1132 va.fillEmptySpans();
1133 }
1134 }
1135
1136 void
Simulate_Span_Allocation()1137 Simulate_Span_Allocation()
1138 {
1139 VolumeAllocator va;
1140
1141 if (VolumeFile.empty()) {
1142 err.push(0, 9, "Volume config file not set");
1143 }
1144 if (SpanFile.empty()) {
1145 err.push(0, 9, "Span file not set");
1146 }
1147
1148 if (err) {
1149 if ((err = va.load(SpanFile, VolumeFile)).isOK()) {
1150 err = va.fillAllSpans();
1151 va.dumpVolumes();
1152 }
1153 }
1154 }
1155
1156 void
Clear_Spans()1157 Clear_Spans()
1158 {
1159 Cache cache;
1160
1161 if (!OPEN_RW_FLAG) {
1162 err.push(0, 1, "Writing Not Enabled.. Please use --write to enable writing to disk");
1163 return;
1164 }
1165
1166 if ((err = cache.loadSpan(SpanFile))) {
1167 for (auto *span : cache._spans) {
1168 span->clearPermanently();
1169 }
1170 }
1171 }
1172
1173 void
Find_Stripe(ts::file::path const & input_file_path)1174 Find_Stripe(ts::file::path const &input_file_path)
1175 {
1176 // scheme=http user=u password=p host=172.28.56.109 path=somepath query=somequery port=1234
1177 // input file format: scheme://hostname:port/somepath;params?somequery user=USER password=PASS
1178 // user, password, are optional; scheme and host are required
1179
1180 // char* h= http://user:pass@IPADDRESS/path_to_file;?port <== this is the format we need
1181 // url_matcher matcher;
1182 // if (matcher.match(h))
1183 // std::cout << h << " : is valid" << std::endl;
1184 // else
1185 // std::cout << h << " : is NOT valid" << std::endl;
1186
1187 Cache cache;
1188 if (!input_file_path.empty()) {
1189 printf("passed argv %s\n", input_file_path.c_str());
1190 }
1191 cache.loadURLs(input_file_path);
1192 if ((err = cache.loadSpan(SpanFile))) {
1193 cache.dumpSpans(Cache::SpanDumpDepth::SPAN);
1194 cache.build_stripe_hash_table();
1195 for (auto host : cache.URLset) {
1196 CryptoContext ctx;
1197 CryptoHash hashT;
1198 ts::LocalBufferWriter<33> w;
1199 ctx.update(host->url.data(), host->url.size());
1200 ctx.update(&host->port, sizeof(host->port));
1201 ctx.finalize(hashT);
1202 Stripe *stripe_ = cache.key_to_stripe(&hashT, host->url.data(), host->url.size());
1203 w.print("{}", hashT);
1204 printf("hash of %.*s is %.*s: Stripe %s \n", static_cast<int>(host->url.size()), host->url.data(),
1205 static_cast<int>(w.size()), w.data(), stripe_->hashText.data());
1206 }
1207 }
1208 }
1209
1210 void
dir_check()1211 dir_check()
1212 {
1213 Cache cache;
1214 if ((err = cache.loadSpan(SpanFile))) {
1215 cache.dumpSpans(Cache::SpanDumpDepth::SPAN);
1216 for (auto &stripe : cache.globalVec_stripe) {
1217 stripe->dir_check();
1218 }
1219 }
1220 printf("\nCHECK succeeded\n");
1221 }
1222
1223 void
walk_bucket_chain(const std::string & devicePath)1224 walk_bucket_chain(const std::string &devicePath)
1225 {
1226 Cache cache;
1227 if ((err = cache.loadSpan(SpanFile))) {
1228 cache.dumpSpans(Cache::SpanDumpDepth::SPAN);
1229 for (auto sp : cache._spans) {
1230 if (devicePath.size() > 0 && sp->_path.view() == devicePath) {
1231 for (auto strp : sp->_stripes) {
1232 strp->loadMeta();
1233 strp->loadDir();
1234 strp->walk_all_buckets();
1235 }
1236 }
1237 }
1238 }
1239 }
1240
1241 void
Clear_Span(const std::string & devicePath)1242 Clear_Span(const std::string &devicePath)
1243 {
1244 Cache cache;
1245 if ((err = cache.loadSpan(SpanFile))) {
1246 cache.dumpSpans(Cache::SpanDumpDepth::SPAN);
1247 for (auto sp : cache._spans) {
1248 if (devicePath.size() > 0 && sp->_path.view() == devicePath) {
1249 printf("clearing %s\n", devicePath.data());
1250 sp->clearPermanently();
1251 }
1252 }
1253 }
1254 return;
1255 }
1256
1257 void
Check_Freelist(const std::string & devicePath)1258 Check_Freelist(const std::string &devicePath)
1259 {
1260 Cache cache;
1261 if ((err = cache.loadSpan(SpanFile))) {
1262 cache.dumpSpans(Cache::SpanDumpDepth::SPAN);
1263 for (auto sp : cache._spans) {
1264 if (devicePath.size() > 0 && sp->_path.view() == devicePath) {
1265 printf("Scanning %s\n", devicePath.data());
1266 for (auto strp : sp->_stripes) {
1267 strp->loadMeta();
1268 strp->loadDir();
1269 for (int s = 0; s < strp->_segments; s++) {
1270 strp->check_loop(s);
1271 }
1272 }
1273 break;
1274 }
1275 }
1276 }
1277 }
1278
1279 void
Init_disk(ts::file::path const & input_file_path)1280 Init_disk(ts::file::path const &input_file_path)
1281 {
1282 Cache cache;
1283 VolumeAllocator va;
1284
1285 if (!OPEN_RW_FLAG) {
1286 err.push(0, 1, "Writing Not Enabled.. Please use --write to enable writing to disk");
1287 return;
1288 }
1289
1290 err = va.load(SpanFile, VolumeFile);
1291 va.allocateSpan(input_file_path);
1292 }
1293
1294 void
Get_Response(ts::file::path const & input_file_path)1295 Get_Response(ts::file::path const &input_file_path)
1296 {
1297 // scheme=http user=u password=p host=172.28.56.109 path=somepath query=somequery port=1234
1298 // input file format: scheme://hostname:port/somepath;params?somequery user=USER password=PASS
1299 // user, password, are optional; scheme and host are required
1300
1301 // char* h= http://user:pass@IPADDRESS/path_to_file;?port <== this is the format we need
1302
1303 Cache cache;
1304 if (!input_file_path.empty()) {
1305 printf("passed argv %s\n", input_file_path.c_str());
1306 }
1307 cache.loadURLs(input_file_path);
1308 if ((err = cache.loadSpan(SpanFile))) {
1309 cache.dumpSpans(Cache::SpanDumpDepth::SPAN);
1310 cache.build_stripe_hash_table();
1311 for (auto host : cache.URLset) {
1312 CryptoContext ctx;
1313 CryptoHash hashT;
1314 ts::LocalBufferWriter<33> w;
1315 ctx.update(host->url.data(), host->url.size());
1316 ctx.update(&host->port, sizeof(host->port));
1317 ctx.finalize(hashT);
1318 Stripe *stripe_ = cache.key_to_stripe(&hashT, host->url.data(), host->url.size());
1319 w.print("{}", hashT);
1320 printf("hash of %.*s is %.*s: Stripe %s \n", static_cast<int>(host->url.size()), host->url.data(),
1321 static_cast<int>(w.size()), w.data(), stripe_->hashText.data());
1322 CacheDirEntry *dir_result = nullptr;
1323 stripe_->loadMeta();
1324 stripe_->loadDir();
1325 stripe_->dir_probe(&hashT, dir_result, nullptr);
1326 }
1327 }
1328 }
1329
scan_span(Span * span,ts::file::path const & regex_path)1330 void static scan_span(Span *span, ts::file::path const ®ex_path)
1331 {
1332 for (auto strp : span->_stripes) {
1333 strp->loadMeta();
1334 strp->loadDir();
1335
1336 if (!regex_path.empty()) {
1337 CacheScan cs(strp, regex_path);
1338 cs.Scan(true);
1339 } else {
1340 CacheScan cs(strp);
1341 cs.Scan(false);
1342 }
1343 }
1344 }
1345
1346 void
Scan_Cache(ts::file::path const & regex_path)1347 Scan_Cache(ts::file::path const ®ex_path)
1348 {
1349 Cache cache;
1350 std::vector<std::thread> threadPool;
1351 if ((err = cache.loadSpan(SpanFile))) {
1352 if (err.size()) {
1353 return;
1354 }
1355 cache.dumpSpans(Cache::SpanDumpDepth::SPAN);
1356 for (auto sp : cache._spans) {
1357 threadPool.emplace_back(scan_span, sp, regex_path);
1358 }
1359 for (auto &th : threadPool) {
1360 th.join();
1361 }
1362 }
1363 }
1364
1365 int
main(int argc,const char * argv[])1366 main(int argc, const char *argv[])
1367 {
1368 ts::file::path input_url_file;
1369 std::string inputFile;
1370
1371 parser.add_global_usage(std::string(argv[0]) + " --spans <SPAN> --volume <FILE> <COMMAND> [<SUBCOMMAND> ...]\n");
1372 parser.require_commands()
1373 .add_option("--help", "-h", "")
1374 .add_option("--spans", "-s", "", "", 1)
1375 .add_option("--volumes", "-v", "", "", 1)
1376 .add_option("--write", "-w", "")
1377 .add_option("--input", "-i", "", "", 1)
1378 .add_option("--device", "-d", "", "", 1)
1379 .add_option("--aos", "-o", "", "", 1);
1380
1381 parser.add_command("list", "List elements of the cache", []() { List_Stripes(Cache::SpanDumpDepth::SPAN); })
1382 .add_command("stripes", "List the stripes", []() { List_Stripes(Cache::SpanDumpDepth::STRIPE); });
1383 parser.add_command("clear", "Clear spans", []() { Clear_Spans(); }).add_command("span", "clear an specific span", [&]() {
1384 Clear_Span(inputFile);
1385 });
1386 auto &c = parser.add_command("dir_check", "cache check").require_commands();
1387 c.add_command("full", "Full report of the cache storage", &dir_check);
1388 c.add_command("freelist", "check the freelist for loop", [&]() { Check_Freelist(inputFile); });
1389 c.add_command("bucket_chain", "walk bucket chains for loops", [&]() { walk_bucket_chain(inputFile); });
1390 parser.add_command("volumes", "Volumes", &Simulate_Span_Allocation);
1391 parser.add_command("alloc", "Storage allocation")
1392 .require_commands()
1393 .add_command("free", "Allocate storage on free (empty) spans", &Cmd_Allocate_Empty_Spans);
1394 parser.add_command("find", "Find Stripe Assignment", [&]() { Find_Stripe(input_url_file); });
1395 parser.add_command("clearspan", "clear specific span").add_command("span", "device path", [&]() { Clear_Span(inputFile); });
1396 parser.add_command("retrieve", " retrieve the response of the given list of URLs", [&]() { Get_Response(input_url_file); });
1397 parser.add_command("init", " Initializes uninitialized span", [&]() { Init_disk(input_url_file); });
1398 parser.add_command("scan", " Scans the whole cache and lists the urls of the cached contents",
1399 [&]() { Scan_Cache(input_url_file); });
1400
1401 // parse the arguments
1402 auto arguments = parser.parse(argv);
1403 if (auto data = arguments.get("spans")) {
1404 SpanFile = data.value();
1405 }
1406 if (auto data = arguments.get("volumes")) {
1407 VolumeFile = data.value();
1408 }
1409 if (auto data = arguments.get("input")) {
1410 input_url_file = data.value();
1411 }
1412 if (auto data = arguments.get("aos")) {
1413 cache_config_min_average_object_size = std::stoi(data.value());
1414 }
1415 if (auto data = arguments.get("device")) {
1416 inputFile = data.value();
1417 }
1418 if (auto data = arguments.get("write")) {
1419 OPEN_RW_FLAG = O_RDWR;
1420 std::cout << "NOTE: Writing to physical devices enabled" << std::endl;
1421 }
1422
1423 if (arguments.has_action()) {
1424 arguments.invoke();
1425 }
1426
1427 if (err.size()) {
1428 std::cerr << err;
1429 exit(1);
1430 }
1431
1432 return 0;
1433 }
1434