This is an automated email from the ASF dual-hosted git repository.
jvanderzee pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new cab8fb02b4 Add a constructor to `Stripe` to impose invariants early
(#11811)
cab8fb02b4 is described below
commit cab8fb02b46650132b5e54b8ef6a5607800ac216
Author: JosiahWI <[email protected]>
AuthorDate: Tue Oct 15 21:32:57 2024 -0500
Add a constructor to `Stripe` to impose invariants early (#11811)
* Add Stripe::Stripe and use it in unit tests
* Initialize disk related metadata in constructor
* Update cache regression test for new constructor
* Start write head at correct buffer position
* Free all allocated buffers
One of the test cases that didn't previously allocate a Stripe directory
now has to free the one allocated in the constructor. Also, we have to free
the disk path that we now allocate in the constructor.
---
src/iocore/cache/Cache.cc | 8 +-
src/iocore/cache/CacheTest.cc | 16 ++--
src/iocore/cache/Stripe.cc | 140 +++++++++++++++++++----------
src/iocore/cache/Stripe.h | 18 +++-
src/iocore/cache/StripeSM.cc | 36 +++-----
src/iocore/cache/StripeSM.h | 20 +++--
src/iocore/cache/unit_tests/test_Stripe.cc | 52 +++++++----
7 files changed, 181 insertions(+), 109 deletions(-)
diff --git a/src/iocore/cache/Cache.cc b/src/iocore/cache/Cache.cc
index a53afbf849..c9461d5b4f 100644
--- a/src/iocore/cache/Cache.cc
+++ b/src/iocore/cache/Cache.cc
@@ -269,16 +269,14 @@ Cache::open(bool clear, bool /* fix ATS_UNUSED */)
if (cp->disk_stripes[i] && !DISK_BAD(cp->disk_stripes[i]->disk)) {
DiskStripeBlockQueue *q = cp->disk_stripes[i]->dpb_queue.head;
for (; q; q = q->link.next) {
- cp->stripes[vol_no] = new StripeSM();
+ blocks = q->b->len;
CacheDisk *d = cp->disk_stripes[i]->disk;
- cp->stripes[vol_no]->disk = d;
- cp->stripes[vol_no]->fd = d->fd;
+ cp->stripes[vol_no] = new StripeSM(d, blocks,
q->b->offset);
cp->stripes[vol_no]->cache = this;
cp->stripes[vol_no]->cache_vol = cp;
- blocks = q->b->len;
bool vol_clear = clear || d->cleared || q->new_block;
- cp->stripes[vol_no]->init(d->path, blocks, q->b->offset,
vol_clear);
+ cp->stripes[vol_no]->init(vol_clear);
vol_no++;
cache_size += blocks;
}
diff --git a/src/iocore/cache/CacheTest.cc b/src/iocore/cache/CacheTest.cc
index 32d6c44942..3572b2f059 100644
--- a/src/iocore/cache/CacheTest.cc
+++ b/src/iocore/cache/CacheTest.cc
@@ -435,7 +435,7 @@
REGRESSION_TEST(cache_disk_replacement_stability)(RegressionTest *t, int level,
CacheHostRecord hr1, hr2;
StripeSM *sample;
static int const sample_idx = 16;
- StripeSM stripes[MAX_VOLS];
+ StripeSM *stripes[MAX_VOLS];
StripeSM *stripe_ptrs[MAX_VOLS]; // array of pointers.
char buff[2048];
@@ -450,11 +450,10 @@
REGRESSION_TEST(cache_disk_replacement_stability)(RegressionTest *t, int level,
disk.num_errors = 0;
for (int i = 0; i < MAX_VOLS; ++i) {
- stripe_ptrs[i] = stripes + i;
- stripes[i].disk = &disk;
- stripes[i].len = DEFAULT_STRIPE_SIZE;
- snprintf(buff, sizeof(buff), "/dev/sd%c %" PRIu64 ":%" PRIu64, 'a' + i,
DEFAULT_SKIP, stripes[i].len);
- CryptoContext().hash_immediate(stripes[i].hash_id, buff, strlen(buff));
+ stripes[i] = new StripeSM{&disk,
static_cast<off_t>(DEFAULT_STRIPE_SIZE / STORE_BLOCK_SIZE), 0};
+ stripe_ptrs[i] = stripes[i];
+ snprintf(buff, sizeof(buff), "/dev/sd%c %" PRIu64 ":%" PRIu64, 'a' + i,
DEFAULT_SKIP, stripes[i]->len);
+ CryptoContext().hash_immediate(stripes[i]->hash_id, buff, strlen(buff));
}
hr1.vol_hash_table = nullptr;
@@ -466,7 +465,7 @@
REGRESSION_TEST(cache_disk_replacement_stability)(RegressionTest *t, int level,
hr2.stripes = stripe_ptrs;
hr2.num_vols = MAX_VOLS;
- sample = stripes + sample_idx;
+ sample = stripes[sample_idx];
sample->len = 1024ULL * 1024 * 1024 * (1024 + 128); // 1.1 TB
snprintf(buff, sizeof(buff), "/dev/sd%c %" PRIu64 ":%" PRIu64, 'a' +
sample_idx, DEFAULT_SKIP, sample->len);
CryptoContext().hash_immediate(sample->hash_id, buff, strlen(buff));
@@ -498,6 +497,9 @@
REGRESSION_TEST(cache_disk_replacement_stability)(RegressionTest *t, int level,
hr1.stripes = nullptr;
hr2.stripes = nullptr;
+ for (StripeSM *stripe : stripes) {
+ delete stripe;
+ }
}
static double zipf_alpha = 1.2;
diff --git a/src/iocore/cache/Stripe.cc b/src/iocore/cache/Stripe.cc
index 334c28083a..628249643e 100644
--- a/src/iocore/cache/Stripe.cc
+++ b/src/iocore/cache/Stripe.cc
@@ -21,13 +21,17 @@
limitations under the License.
*/
+#include "P_CacheDisk.h"
#include "P_CacheInternal.h"
#include "StripeSM.h"
+#include "tsutil/DbgCtl.h"
+
#include "tscore/hugepages.h"
#include "tscore/ink_assert.h"
#include "tscore/ink_memory.h"
+#include <cstddef>
#include <cstring>
using CacheHTTPInfo = HTTPInfo;
@@ -35,12 +39,23 @@ using CacheHTTPInfo = HTTPInfo;
namespace
{
+DbgCtl dbg_ctl_cache_init{"cache_init"};
+
+constexpr int
DIRECTORY_FOOTER_SIZE{ROUND_TO_STORE_BLOCK(sizeof(StripteHeaderFooter))};
+
int
compare_ushort(void const *a, void const *b)
{
return *static_cast<unsigned short const *>(a) - *static_cast<unsigned short
const *>(b);
}
+template <typename T, typename U>
+constexpr double
+percent(T part, U whole)
+{
+ return static_cast<double>(part) / static_cast<double>(whole) * 100.0;
+}
+
} // namespace
struct StripeInitInfo {
@@ -69,6 +84,84 @@ struct StripeInitInfo {
// Stripe
//
+Stripe::Stripe(CacheDisk *disk, off_t blocks, off_t dir_skip)
+ : path{ats_strdup(disk->path)},
+ fd{disk->fd},
+ skip{ROUND_TO_STORE_BLOCK((dir_skip < START_POS ? START_POS : dir_skip))},
+ start{skip},
+ len{blocks * STORE_BLOCK_SIZE},
+ disk{disk}
+{
+ ink_assert(this->len < MAX_STRIPE_SIZE);
+
+ this->_init_hash_text(disk->path, blocks, dir_skip);
+ this->_init_data(STORE_BLOCK_SIZE);
+ this->_init_directory(this->dirlen(), this->headerlen(),
DIRECTORY_FOOTER_SIZE);
+}
+
+void
+Stripe::_init_hash_text(char const *seed, off_t blocks, off_t dir_skip)
+{
+ char const *seed_str = this->disk->hash_base_string ?
this->disk->hash_base_string : seed;
+ const size_t hash_seed_size = strlen(seed_str);
+ const size_t hash_text_size = hash_seed_size + 32;
+
+ this->hash_text = static_cast<char *>(ats_malloc(hash_text_size));
+ ink_strlcpy(hash_text, seed_str, hash_text_size);
+ snprintf(hash_text + hash_seed_size, (hash_text_size - hash_seed_size), " %"
PRIu64 ":%" PRIu64 "",
+ static_cast<uint64_t>(dir_skip), static_cast<uint64_t>(blocks));
+}
+
+void
+Stripe::_init_data(off_t store_block_size)
+{
+ // iteratively calculate start + buckets; updates this->start
+ this->_init_data_internal();
+ this->_init_data_internal();
+ this->_init_data_internal();
+
+ this->data_blocks = (this->len - (this->start - this->skip)) /
store_block_size;
+}
+
+void
+Stripe::_init_data_internal()
+{
+ // step1: calculate the number of entries.
+ off_t total_entries = (this->len - (this->start - this->skip)) /
cache_config_min_average_object_size;
+ // step2: calculate the number of buckets
+ off_t total_buckets = total_entries / DIR_DEPTH;
+ // step3: calculate the number of segments, no segment has more than 16384
buckets
+ this->segments = (total_buckets + (((1 << 16) - 1) / DIR_DEPTH)) / ((1 <<
16) / DIR_DEPTH);
+ // step4: divide total_buckets into segments on average.
+ this->buckets = (total_buckets + this->segments - 1) / this->segments;
+ // step5: set the start pointer.
+ this->start = this->skip + 2 * this->dirlen();
+}
+
+void
+Stripe::_init_directory(std::size_t directory_size, int header_size, int
footer_size)
+{
+ ink_assert(directory_size <= static_cast<std::size_t>(this->len));
+ // It's probably invalid for the directory to be this small, but at least we
+ // know we will allocate sufficient space for the data we initialize
+ // pointers to, and we can't corrupt our dir pointer by writing to the
+ // footer.
+ ink_release_assert(directory_size >= sizeof(Dir) + header_size +
footer_size);
+
+ Dbg(dbg_ctl_cache_init, "Stripe %s: allocating %zu directory bytes for a
%lld byte volume (%lf%%)", hash_text.get(),
+ directory_size, (long long)this->len, percent(directory_size,
this->len));
+ if (ats_hugepage_enabled()) {
+ this->raw_dir = static_cast<char *>(ats_alloc_hugepage(directory_size));
+ }
+ if (nullptr == this->raw_dir) {
+ this->raw_dir = static_cast<char *>(ats_memalign(ats_pagesize(),
directory_size));
+ }
+ this->dir = reinterpret_cast<Dir *>(this->raw_dir + header_size);
+ this->header = reinterpret_cast<StripteHeaderFooter *>(this->raw_dir);
+ std::size_t const footer_offset{directory_size -
static_cast<std::size_t>(footer_size)};
+ this->footer = reinterpret_cast<StripteHeaderFooter *>(this->raw_dir +
footer_offset);
+}
+
int
Stripe::dir_check()
{
@@ -261,53 +354,6 @@ Stripe::_init_dir()
}
}
-void
-Stripe::_init_data_internal()
-{
- // step1: calculate the number of entries.
- off_t total_entries = (this->len - (this->start - this->skip)) /
cache_config_min_average_object_size;
- // step2: calculate the number of buckets
- off_t total_buckets = total_entries / DIR_DEPTH;
- // step3: calculate the number of segments, no segment has more than 16384
buckets
- this->segments = (total_buckets + (((1 << 16) - 1) / DIR_DEPTH)) / ((1 <<
16) / DIR_DEPTH);
- // step4: divide total_buckets into segments on average.
- this->buckets = (total_buckets + this->segments - 1) / this->segments;
- // step5: set the start pointer.
- this->start = this->skip + 2 * this->dirlen();
-}
-
-void
-Stripe::_init_data(off_t blocks, off_t dir_skip)
-{
- len = blocks * STORE_BLOCK_SIZE;
- ink_assert(len <= MAX_STRIPE_SIZE);
-
- skip = ROUND_TO_STORE_BLOCK((dir_skip < START_POS ? START_POS : dir_skip));
-
- // successive approximation, directory/meta data eats up some storage
- start = skip;
-
- // iteratively calculate start + buckets
- this->_init_data_internal();
- this->_init_data_internal();
- this->_init_data_internal();
-
- data_blocks = (len - (start - skip)) / STORE_BLOCK_SIZE;
-
- // raw_dir
- raw_dir = nullptr;
- if (ats_hugepage_enabled()) {
- raw_dir = static_cast<char *>(ats_alloc_hugepage(this->dirlen()));
- }
- if (raw_dir == nullptr) {
- raw_dir = static_cast<char *>(ats_memalign(ats_pagesize(),
this->dirlen()));
- }
-
- dir = reinterpret_cast<Dir *>(raw_dir + this->headerlen());
- header = reinterpret_cast<StripteHeaderFooter *>(raw_dir);
- footer = reinterpret_cast<StripteHeaderFooter *>(raw_dir + this->dirlen() -
ROUND_TO_STORE_BLOCK(sizeof(StripteHeaderFooter)));
-}
-
bool
Stripe::flush_aggregate_write_buffer()
{
diff --git a/src/iocore/cache/Stripe.h b/src/iocore/cache/Stripe.h
index 6f9f877a58..ebf6e46dba 100644
--- a/src/iocore/cache/Stripe.h
+++ b/src/iocore/cache/Stripe.h
@@ -33,6 +33,7 @@
#include "tscore/ink_align.h"
#include "tscore/ink_memory.h"
+#include <cstddef>
#include <cstdint>
#define CACHE_BLOCK_SHIFT 9
@@ -102,6 +103,19 @@ public:
CacheVol *cache_vol{};
+ /**
+ * Stripe constructor.
+ *
+ * @param disk: The disk object to associate with this stripe.
+ * The disk path must be non-null.
+ * @param blocks: Number of blocks. Must be at least 10.
+ * @param dir_skip: Offset into the disk at which to start the stripe.
+ * If this value is less than START_POS, START_POS will be used instead.
+ *
+ * @see START_POS
+ */
+ Stripe(CacheDisk *disk, off_t blocks, off_t dir_skip);
+
int dir_check();
uint32_t round_to_approx_size(uint32_t l) const;
@@ -158,11 +172,13 @@ protected:
void _clear_init();
void _init_dir();
- void _init_data(off_t blocks, off_t dir_skip);
bool flush_aggregate_write_buffer();
private:
+ void _init_hash_text(char const *seed, off_t blocks, off_t dir_skip);
+ void _init_data(off_t store_block_size);
void _init_data_internal();
+ void _init_directory(std::size_t directory_size, int header_size, int
footer_size);
};
inline uint32_t
diff --git a/src/iocore/cache/StripeSM.cc b/src/iocore/cache/StripeSM.cc
index f5861df320..d3c901ec59 100644
--- a/src/iocore/cache/StripeSM.cc
+++ b/src/iocore/cache/StripeSM.cc
@@ -109,6 +109,17 @@ struct StripeInitInfo {
}
};
+StripeSM::StripeSM(CacheDisk *disk, off_t blocks, off_t dir_skip) :
Continuation(new_ProxyMutex()), Stripe{disk, blocks, dir_skip}
+{
+ this->_preserved_dirs.evacuate_size = static_cast<int>(len /
EVACUATION_BUCKET_SIZE) + 2;
+ int evac_len = this->_preserved_dirs.evacuate_size *
sizeof(DLL<EvacuationBlock>);
+ this->_preserved_dirs.evacuate = static_cast<DLL<EvacuationBlock>
*>(ats_malloc(evac_len));
+ memset(static_cast<void *>(this->_preserved_dirs.evacuate), 0, evac_len);
+
+ open_dir.mutex = this->mutex;
+ SET_HANDLER(&StripeSM::aggWrite);
+}
+
int
StripeSM::begin_read(CacheVC *cont) const
{
@@ -151,36 +162,13 @@ StripeSM::clear_dir()
}
int
-StripeSM::init(char *s, off_t blocks, off_t dir_skip, bool clear)
+StripeSM::init(bool clear)
{
- // Hash
- char *seed_str = disk->hash_base_string ?
disk->hash_base_string : s;
- const size_t hash_seed_size = strlen(seed_str);
- const size_t hash_text_size = hash_seed_size + 32;
-
- hash_text = static_cast<char *>(ats_malloc(hash_text_size));
- ink_strlcpy(hash_text, seed_str, hash_text_size);
- snprintf(hash_text + hash_seed_size, (hash_text_size - hash_seed_size), " %"
PRIu64 ":%" PRIu64 "",
- static_cast<uint64_t>(dir_skip), static_cast<uint64_t>(blocks));
CryptoContext().hash_immediate(hash_id, hash_text, strlen(hash_text));
- path = ats_strdup(s);
-
- // Stripe
- this->_init_data(blocks, dir_skip);
-
// Evacuation
this->hit_evacuate_window = (this->data_blocks *
cache_config_hit_evacuate_percent) / 100;
- // PreservationTable
- this->_preserved_dirs.evacuate_size = static_cast<int>(len /
EVACUATION_BUCKET_SIZE) + 2;
- int evac_len = this->_preserved_dirs.evacuate_size *
sizeof(DLL<EvacuationBlock>);
- this->_preserved_dirs.evacuate = static_cast<DLL<EvacuationBlock>
*>(ats_malloc(evac_len));
- memset(static_cast<void *>(this->_preserved_dirs.evacuate), 0, evac_len);
-
- Dbg(dbg_ctl_cache_init, "Vol %s: allocating %zu directory bytes for a %lld
byte volume (%lf%%)", hash_text.get(), dirlen(),
- static_cast<long long>(this->len), static_cast<double>(dirlen()) /
static_cast<double>(this->len) * 100.0);
-
// AIO
if (clear) {
Note("clearing cache directory '%s'", hash_text.get());
diff --git a/src/iocore/cache/StripeSM.h b/src/iocore/cache/StripeSM.h
index 7ee9515392..685942c49d 100644
--- a/src/iocore/cache/StripeSM.h
+++ b/src/iocore/cache/StripeSM.h
@@ -24,6 +24,7 @@
#pragma once
#include "P_CacheDir.h"
+#include "P_CacheDisk.h"
#include "P_RamCache.h"
#include "AggregateWriteBuffer.h"
#include "PreservationTable.h"
@@ -111,7 +112,7 @@ public:
int clear_dir_aio();
int clear_dir();
- int init(char *s, off_t blocks, off_t dir_skip, bool clear);
+ int init(bool clear);
int handle_dir_clear(int event, void *data);
int handle_dir_read(int event, void *data);
@@ -156,11 +157,18 @@ public:
int within_hit_evacuate_window(Dir const *dir) const;
- StripeSM() : Continuation(new_ProxyMutex())
- {
- open_dir.mutex = mutex;
- SET_HANDLER(&StripeSM::aggWrite);
- }
+ /**
+ * StripeSM constructor.
+ *
+ * @param disk: The disk object to associate with this stripe.
+ * The disk path must be non-null.
+ * @param blocks: Number of blocks. Must be at least 10.
+ * @param dir_skip: Offset into the disk at which to start the stripe.
+ * If this value is less than START_POS, START_POS will be used instead.
+ *
+ * @see START_POS
+ */
+ StripeSM(CacheDisk *disk, off_t blocks, off_t dir_skip);
Queue<CacheVC, Continuation::Link_link> &get_pending_writers();
diff --git a/src/iocore/cache/unit_tests/test_Stripe.cc
b/src/iocore/cache/unit_tests/test_Stripe.cc
index 34baca524e..6c09b1af00 100644
--- a/src/iocore/cache/unit_tests/test_Stripe.cc
+++ b/src/iocore/cache/unit_tests/test_Stripe.cc
@@ -81,6 +81,17 @@ std::array<AddWriterBranchTest, 32>
add_writer_branch_test_cases = {
}
};
+static void
+init_disk(CacheDisk &disk)
+{
+ disk.path = static_cast<char *>(ats_malloc(1));
+ disk.path[0] = '\0';
+ disk.disk_stripes = static_cast<DiskStripe
**>(ats_malloc(sizeof(DiskStripe *)));
+ disk.disk_stripes[0] = nullptr;
+ disk.header = static_cast<DiskHeader
*>(ats_malloc(sizeof(DiskHeader)));
+ disk.header->num_volumes = 0;
+}
+
/* Catch test helper to provide a StripeSM with a valid file descriptor.
*
* The file will be deleted automatically when the application ends normally.
@@ -113,22 +124,13 @@ init_stripe_for_writing(StripeSM &stripe,
StripteHeaderFooter &header, CacheVol
cache_rsb.gc_frags_evacuated =
Metrics::Counter::createPtr("unit_test.gc.frags.evacuated");
stripe.cache_vol->vol_rsb.gc_frags_evacuated =
Metrics::Counter::createPtr("unit_test.gc.frags.evacuated");
- // A number of things must be initialized in a certain way for Stripe
- // not to segfault, hit an assertion, or exhibit zero-division.
- // I just picked values that happen to work.
stripe.sector_size = 256;
- stripe.skip = 0;
- stripe.len = 600000000000000;
- stripe.segments = 1;
- stripe.buckets = 4;
- stripe.start = stripe.skip + 2 * stripe.dirlen();
- stripe.raw_dir = static_cast<char *>(ats_memalign(ats_pagesize(),
stripe.dirlen()));
- stripe.dir = reinterpret_cast<Dir *>(stripe.raw_dir +
stripe.headerlen());
-
- stripe.get_preserved_dirs().evacuate = static_cast<DLL<EvacuationBlock>
*>(ats_malloc(2024));
- memset(static_cast<void *>(stripe.get_preserved_dirs().evacuate), 0, 2024);
-
- header.write_pos = 50000;
+
+ // This is the minimum value for header.write_pos. Offsets are calculated
+ // based on the distance of the write_pos from this point. If we ever move
+ // the write head before the start of the stripe data section, we will
+ // underflow offset calculations and end up in big trouble.
+ header.write_pos = stripe.start;
header.agg_pos = 1;
header.phase = 0;
stripe.header = &header;
@@ -137,8 +139,10 @@ init_stripe_for_writing(StripeSM &stripe,
StripteHeaderFooter &header, CacheVol
TEST_CASE("The behavior of StripeSM::add_writer.")
{
- FakeVC vc;
- StripeSM stripe;
+ FakeVC vc;
+ CacheDisk disk;
+ init_disk(disk);
+ StripeSM stripe{&disk, 10, 0};
SECTION("Branch tests.")
{
@@ -187,6 +191,10 @@ TEST_CASE("The behavior of StripeSM::add_writer.")
CHECK(true == result);
}
}
+
+ ats_free(stripe.raw_dir);
+ ats_free(stripe.get_preserved_dirs().evacuate);
+ ats_free(stripe.path);
}
// This test case demonstrates how to set up a StripeSM and make
@@ -194,7 +202,9 @@ TEST_CASE("The behavior of StripeSM::add_writer.")
// tmpfile for the StripeSM to write to.
TEST_CASE("aggWrite behavior with f.evacuator unset")
{
- StripeSM stripe;
+ CacheDisk disk;
+ init_disk(disk);
+ StripeSM stripe{&disk, 10, 0};
StripteHeaderFooter header;
CacheVol cache_vol;
auto *file{init_stripe_for_writing(stripe, header, cache_vol)};
@@ -296,6 +306,7 @@ TEST_CASE("aggWrite behavior with f.evacuator unset")
ats_free(stripe.raw_dir);
ats_free(stripe.get_preserved_dirs().evacuate);
+ ats_free(stripe.path);
}
// When f.evacuator is set, vc.buf must contain a Doc object including headers
@@ -304,7 +315,9 @@ TEST_CASE("aggWrite behavior with f.evacuator unset")
// only on the presence of the f.evacuator flag.
TEST_CASE("aggWrite behavior with f.evacuator set")
{
- StripeSM stripe;
+ CacheDisk disk;
+ init_disk(disk);
+ StripeSM stripe{&disk, 10, 0};
StripteHeaderFooter header;
CacheVol cache_vol;
auto *file{init_stripe_for_writing(stripe, header, cache_vol)};
@@ -393,4 +406,5 @@ TEST_CASE("aggWrite behavior with f.evacuator set")
delete[] source;
ats_free(stripe.raw_dir);
ats_free(stripe.get_preserved_dirs().evacuate);
+ ats_free(stripe.path);
}