Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 24 additions & 73 deletions src/iocore/cache/CacheDir.cc
Original file line number Diff line number Diff line change
Expand Up @@ -210,28 +210,28 @@ dir_bucket_loop_check(Dir *start_dir, Dir *seg)
// adds all the directory entries
// in a segment to the segment freelist
void
dir_init_segment(int s, Directory *directory)
Directory::init_segment(int s)
{
directory->header->freelist[s] = 0;
Dir *seg = directory->get_segment(s);
this->header->freelist[s] = 0;
Dir *seg = this->get_segment(s);
int l, b;
memset(static_cast<void *>(seg), 0, SIZEOF_DIR * DIR_DEPTH * directory->buckets);
memset(static_cast<void *>(seg), 0, SIZEOF_DIR * DIR_DEPTH * this->buckets);
for (l = 1; l < DIR_DEPTH; l++) {
for (b = 0; b < directory->buckets; b++) {
for (b = 0; b < this->buckets; b++) {
Dir *bucket = dir_bucket(b, seg);
directory->free_entry(dir_bucket_row(bucket, l), s);
this->free_entry(dir_bucket_row(bucket, l), s);
}
}
}

// break the infinite loop in directory entries
// Note : abuse of the token bit in dir entries
int
dir_bucket_loop_fix(Dir *start_dir, int s, Directory *directory)
Directory::bucket_loop_fix(Dir *start_dir, int s)
{
if (!dir_bucket_loop_check(start_dir, directory->get_segment(s))) {
if (!dir_bucket_loop_check(start_dir, this->get_segment(s))) {
Warning("Dir loop exists, clearing segment %d", s);
dir_init_segment(s, directory);
this->init_segment(s);
return 1;
}
return 0;
Expand All @@ -243,7 +243,7 @@ Directory::freelist_length(int s)
int free = 0;
Dir *seg = this->get_segment(s);
Dir *e = dir_from_offset(this->header->freelist[s], seg);
if (dir_bucket_loop_fix(e, s, this)) {
if (this->bucket_loop_fix(e, s)) {
return (DIR_DEPTH - 1) * this->buckets;
}
while (e) {
Expand All @@ -260,7 +260,7 @@ Directory::bucket_length(Dir *b, int s)
int i = 0;
Dir *seg = this->get_segment(s);
#ifdef LOOP_CHECK_MODE
if (dir_bucket_loop_fix(b, s, this))
if (this->bucket_loop_fix(b, s, this))
return 1;
Comment on lines 262 to 264
#endif
while (e) {
Expand Down Expand Up @@ -296,55 +296,6 @@ Directory::check()
return 1;
}

inline void
unlink_from_freelist(Dir *e, int s, Directory *directory)
{
Dir *seg = directory->get_segment(s);
Dir *p = dir_from_offset(dir_prev(e), seg);
if (p) {
dir_set_next(p, dir_next(e));
} else {
directory->header->freelist[s] = dir_next(e);
}
Dir *n = dir_from_offset(dir_next(e), seg);
if (n) {
dir_set_prev(n, dir_prev(e));
}
}

inline Dir *
dir_delete_entry(Dir *e, Dir *p, int s, Directory *directory)
{
Dir *seg = directory->get_segment(s);
int no = dir_next(e);
directory->header->dirty = 1;
if (p) {
unsigned int fo = directory->header->freelist[s];
unsigned int eo = dir_to_offset(e, seg);
dir_clear(e);
dir_set_next(p, no);
dir_set_next(e, fo);
if (fo) {
dir_set_prev(dir_from_offset(fo, seg), eo);
}
directory->header->freelist[s] = eo;
} else {
Dir *n = next_dir(e, seg);
if (n) {
// "Shuffle" here means that we're copying the second entry's data to the head entry's location, and removing the second entry
// - because the head entry can't be moved.
ATS_PROBE3(cache_dir_shuffle, s, dir_to_offset(e, seg), dir_to_offset(n, seg));
dir_assign(e, n);
dir_delete_entry(n, e, s, directory);
return e;
} else {
dir_clear(e);
return nullptr;
}
}
return dir_from_offset(no, seg);
}

inline void
dir_clean_bucket(Dir *b, int s, StripeSM *stripe)
{
Expand All @@ -357,7 +308,7 @@ dir_clean_bucket(Dir *b, int s, StripeSM *stripe)
#ifdef LOOP_CHECK_MODE
loop_count++;
if (loop_count > DIR_LOOP_THRESHOLD) {
if (dir_bucket_loop_fix(b, s, vol->directory))
if (vol->directory.bucket_loop_fix(b, s))
return;
}
#endif
Expand All @@ -372,7 +323,7 @@ dir_clean_bucket(Dir *b, int s, StripeSM *stripe)
}
// Match cache_dir_remove arguments
ATS_PROBE7(cache_dir_remove_clean_bucket, stripe->fd, s, dir_to_offset(e, seg), dir_offset(e), dir_approx_size(e), 0, 0);
e = dir_delete_entry(e, p, s, &stripe->directory);
e = stripe->directory.delete_entry(e, p, s);
continue;
}
p = e;
Expand Down Expand Up @@ -462,7 +413,7 @@ freelist_pop(int s, StripeSM *stripe)
stripe->directory.header->freelist[s] = dir_next(e);
// if the freelist if bad, punt.
if (dir_offset(e)) {
dir_init_segment(s, &stripe->directory);
stripe->directory.init_segment(s);
return nullptr;
}
Dir *h = dir_from_offset(stripe->directory.header->freelist[s], seg);
Expand Down Expand Up @@ -495,7 +446,7 @@ Directory::probe(const CacheKey *key, StripeSM *stripe, Dir *result, Dir **last_
Dir *e = nullptr, *p = nullptr, *collision = *last_collision;
CHECK_DIR(d);
#ifdef LOOP_CHECK_MODE
if (dir_bucket_loop_fix(dir_bucket(b, seg), s, this))
if (this->bucket_loop_fix(dir_bucket(b, seg), s))
return 0;
#endif
Lagain:
Expand All @@ -506,7 +457,7 @@ Directory::probe(const CacheKey *key, StripeSM *stripe, Dir *result, Dir **last_
ink_assert(dir_offset(e));
// Bug: 51680. Need to check collision before checking
// dir_valid(). In case of a collision, if !dir_valid(), we
// don't want to call dir_delete_entry.
// don't want to call Directory::delete_entry.
if (collision) {
if (collision == e) {
collision = nullptr;
Expand All @@ -533,7 +484,7 @@ Directory::probe(const CacheKey *key, StripeSM *stripe, Dir *result, Dir **last_
ts::Metrics::Gauge::decrement(stripe->cache_vol->vol_rsb.direntries_used);
ATS_PROBE7(cache_dir_remove_invalid, stripe->fd, s, dir_to_offset(e, seg), dir_offset(e), dir_approx_size(e),
key->slice64(0), key->slice64(1));
e = dir_delete_entry(e, p, s, this);
e = this->delete_entry(e, p, s);
continue;
}
} else {
Expand Down Expand Up @@ -585,7 +536,7 @@ Directory::insert(const CacheKey *key, StripeSM *stripe, Dir *to_part)
for (l = 1; l < DIR_DEPTH; l++) {
e = dir_bucket_row(b, l);
if (dir_is_empty(e)) {
unlink_from_freelist(e, s, this);
this->unlink_from_freelist(e, s);
goto Llink;
}
}
Expand Down Expand Up @@ -653,7 +604,7 @@ Directory::overwrite(const CacheKey *key, StripeSM *stripe, Dir *dir, Dir *overw
#ifdef LOOP_CHECK_MODE
loop_count++;
if (loop_count > DIR_LOOP_THRESHOLD && loop_possible) {
if (dir_bucket_loop_fix(b, s, this)) {
if (this->bucket_loop_fix(b, s)) {
loop_possible = false;
goto Lagain;
}
Expand All @@ -679,7 +630,7 @@ Directory::overwrite(const CacheKey *key, StripeSM *stripe, Dir *dir, Dir *overw
for (l = 1; l < DIR_DEPTH; l++) {
e = dir_bucket_row(b, l);
if (dir_is_empty(e)) {
unlink_from_freelist(e, s, this);
this->unlink_from_freelist(e, s);
goto Llink;
}
}
Expand Down Expand Up @@ -733,7 +684,7 @@ Directory::remove(const CacheKey *key, StripeSM *stripe, Dir *del)
#ifdef LOOP_CHECK_MODE
loop_count++;
if (loop_count > DIR_LOOP_THRESHOLD) {
if (dir_bucket_loop_fix(dir_bucket(b, seg), s, this))
if (this->bucket_loop_fix(dir_bucket(b, seg), s, this))
return 0;
Comment on lines +687 to 688
}
#endif
Expand All @@ -743,7 +694,7 @@ Directory::remove(const CacheKey *key, StripeSM *stripe, Dir *del)
ts::Metrics::Gauge::decrement(stripe->cache_vol->vol_rsb.direntries_used);
ATS_PROBE7(cache_dir_remove, stripe->fd, s, dir_to_offset(e, seg), offset, dir_approx_size(e), key->slice64(0),
key->slice64(1));
dir_delete_entry(e, p, s, this);
this->delete_entry(e, p, s);
CHECK_DIR(d);
return 1;
}
Expand Down Expand Up @@ -939,7 +890,7 @@ Directory::entries_used()
sfull = 0;
for (int b = 0; b < this->buckets; b++) {
Dir *e = dir_bucket(b, seg);
if (dir_bucket_loop_fix(e, s, this)) {
if (this->bucket_loop_fix(e, s)) {
sfull = 0;
break;
}
Expand Down Expand Up @@ -1068,7 +1019,7 @@ CacheSync::mainEvent(int event, Event * /* e ATS_UNUSED */)
/* Don't sync the directory to disk if its not dirty. Syncing the
clean directory to disk is also the cause of INKqa07151. Increasing
the serial number causes the cache to recover more data than necessary.
The dirty bit it set in dir_insert, overwrite and dir_delete_entry
The dirty bit it set in dir_insert, overwrite and Directory::delete_entry
*/
if (!stripe->directory.header->dirty) {
Dbg(dbg_ctl_cache_dir_sync, "Dir %s not dirty", stripe->hash_text.get());
Expand Down
5 changes: 1 addition & 4 deletions src/iocore/cache/CacheVC.cc
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,6 @@ next_in_map(Stripe *stripe, char *vol_map, off_t offset)
return new_off + start_offset;
}

// Function in CacheDir.cc that we need for make_vol_map().
int dir_bucket_loop_fix(Dir *start_dir, int s, Directory *directory);

// TODO: If we used a bit vector, we could make a smaller map structure.
// TODO: If we saved a high water mark we could have a smaller buf, and avoid searching it
// when we are asked about the highest interesting offset.
Expand All @@ -137,7 +134,7 @@ make_vol_map(Stripe *stripe)
Dir *seg = stripe->directory.get_segment(s);
for (int b = 0; b < stripe->directory.buckets; b++) {
Dir *e = dir_bucket(b, seg);
if (dir_bucket_loop_fix(e, s, &stripe->directory)) {
if (stripe->directory.bucket_loop_fix(e, s)) {
break;
}
while (e) {
Expand Down
85 changes: 72 additions & 13 deletions src/iocore/cache/P_CacheDir.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
#include "tscore/Version.h"
#include "tscore/hugepages.h"

#include <ts/ats_probe.h>

#include <cstdint>
#include <ctime>

Expand Down Expand Up @@ -286,7 +288,9 @@ struct StripeHeaderFooter {
uint16_t freelist[1];
};

struct Directory {
class Directory
{
public:
char *raw_dir{nullptr};
Dir *dir{};
StripeHeaderFooter *header{};
Expand Down Expand Up @@ -316,19 +320,13 @@ struct Directory {
int bucket_length(Dir *b, int s);
int freelist_length(int s);
void clean_segment(int s, StripeSM *stripe);
};

inline int
Directory::entries() const
{
return this->buckets * DIR_DEPTH * this->segments;
}
void init_segment(int s);
int bucket_loop_fix(Dir *start_dir, int s);
Dir *delete_entry(Dir *e, Dir *p, int s);

inline Dir *
Directory::get_segment(int s) const
{
return reinterpret_cast<Dir *>((reinterpret_cast<char *>(this->dir)) + (s * this->buckets) * DIR_DEPTH * SIZEOF_DIR);
}
private:
void unlink_from_freelist(Dir *e, int s);
};

// Global Functions

Expand Down Expand Up @@ -394,3 +392,64 @@ dir_bucket_row(Dir *b, int64_t i)
{
return dir_in_seg(b, i);
}

inline int
Directory::entries() const
{
return this->buckets * DIR_DEPTH * this->segments;
}

inline Dir *
Directory::get_segment(int s) const
{
return reinterpret_cast<Dir *>((reinterpret_cast<char *>(this->dir)) + (s * this->buckets) * DIR_DEPTH * SIZEOF_DIR);
}

inline void
Directory::unlink_from_freelist(Dir *e, int s)
{
Dir *seg = this->get_segment(s);
Dir *p = dir_from_offset(dir_prev(e), seg);
if (p) {
dir_set_next(p, dir_next(e));
} else {
this->header->freelist[s] = dir_next(e);
}
Dir *n = dir_from_offset(dir_next(e), seg);
if (n) {
dir_set_prev(n, dir_prev(e));
}
}

inline Dir *
Directory::delete_entry(Dir *e, Dir *p, int s)
{
Dir *seg = this->get_segment(s);
int no = dir_next(e);
this->header->dirty = 1;
if (p) {
unsigned int fo = this->header->freelist[s];
unsigned int eo = dir_to_offset(e, seg);
dir_clear(e);
dir_set_next(p, no);
dir_set_next(e, fo);
if (fo) {
dir_set_prev(dir_from_offset(fo, seg), eo);
}
this->header->freelist[s] = eo;
} else {
Dir *n = next_dir(e, seg);
if (n) {
// "Shuffle" here means that we're copying the second entry's data to the head entry's location, and removing the second entry
// - because the head entry can't be moved.
ATS_PROBE3(cache_dir_shuffle, s, dir_to_offset(e, seg), dir_to_offset(n, seg));
dir_assign(e, n);
this->delete_entry(n, e, s);
return e;
} else {
dir_clear(e);
return nullptr;
}
}
return dir_from_offset(no, seg);
}