Skip to content

Commit

Permalink
Merge branch 'refs/heads/upstream-HEAD' into repo-HEAD
Browse files Browse the repository at this point in the history
  • Loading branch information
Delphix Engineering committed Jan 11, 2024
2 parents a59dd0b + c10036e commit bba9820
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 45 deletions.
117 changes: 72 additions & 45 deletions src/kdumpfile/cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,17 @@ struct cache_search {
unsigned gprec; /**< Index of MRU precious ghost entry */
unsigned eprec; /**< End of precious entries; this is the
* index of the first unused entry. */
unsigned uprec; /**< Index of LRU unused precious entry. */
unsigned nuprec; /**< Number of unused precious entries. */
unsigned zprec; /**< Index of the least recently used precious
* entry with zero reference count. */
unsigned nzprec; /**< Number of precious entries with zero
* reference count. */
unsigned gprobe; /**< Index of MRU probed ghost entry */
unsigned eprobe; /**< End of probed entries; this is the
* index of the first unused entry. */
unsigned uprobe; /**< Index of LRU unused probed entry. */
unsigned nuprobe; /**< Number of unused probed entries. */
* index of the last unused entry. */
unsigned zprobe; /**< Index of the least recently used probed
* entry with zero reference count. */
unsigned nzprobe; /**< Number of probed entries with zero
* reference count. */
};

/** Insert an entry to the list after a given position.
Expand Down Expand Up @@ -220,12 +224,12 @@ reuse_cached_entry(struct cache *cache, struct cache_entry *entry,
static struct cache_entry *
evict_probe(struct cache *cache, struct cache_search *cs)
{
struct cache_entry *entry = &cache->ce[cs->uprobe];
struct cache_entry *entry = &cache->ce[cs->zprobe];
if (entry->prev != cs->gprobe) {
if (cs->uprobe == cache->split)
if (cs->zprobe == cache->split)
cache->split = entry->prev;
remove_entry(cache, entry);
add_entry_after(cache, entry, cs->uprobe, cs->gprobe);
add_entry_after(cache, entry, cs->zprobe, cs->gprobe);
}
--cache->nprobe;
++cache->ngprobe;
Expand All @@ -240,52 +244,73 @@ evict_probe(struct cache *cache, struct cache_search *cs)
static struct cache_entry *
evict_prec(struct cache *cache, struct cache_search *cs)
{
struct cache_entry *entry = &cache->ce[cs->uprec];
struct cache_entry *entry = &cache->ce[cs->zprec];
if (entry->next != cs->gprec) {
remove_entry(cache, entry);
add_entry_before(cache, entry, cs->uprec, cs->gprec);
add_entry_before(cache, entry, cs->zprec, cs->gprec);
}
--cache->nprec;
++cache->ngprec;
return entry;
}

/** Re-initialize an entry for different data.
/** Evict a cached entry.
*
* @param cache Cache object.
* @param entry Entry to be reinitialized.
* @param cs Cache search info.
* @param bias Bias towards the probed partition.
* @returns The evicted entry.
*
* Evict an entry from the cache and use its data pointer for @p entry.
* The evicted entry is taken either from the probe partition or from the
* precious partition, depending on the value of @c dprobe.
* precious partition. If both are non-empty, make a choice based on the
* value of @c dprobe.
*/
static void
reinit_entry(struct cache *cache, struct cache_entry *entry,
struct cache_search *cs, unsigned bias)
static struct cache_entry *
evict_entry(struct cache *cache, struct cache_search *cs, unsigned bias)
{
struct cache_entry *entry;

if (cs->nzprobe != 0 &&
(cs->nzprec == 0 || cache->nprobe + bias > cache->dprobe))
entry = evict_probe(cache, cs);
else
entry = evict_prec(cache, cs);
if (cache->entry_cleanup)
cache->entry_cleanup(cache->cleanup_data, entry);
return entry;
}

/** Reclaim a data buffer.
*
* @param cache Cache object.
* @param cs Cache search info.
* @param bias Bias towards the probed partition.
* @returns New data buffer.
*
* Find an unused cache entry with non-NULL data and reclaim that data
* buffer from it. If there are no entries in the unused partition, evict
* an existing entry.
*/
static void *
reclaim_data(struct cache *cache, struct cache_search *cs,
unsigned bias)
{
struct cache_entry *evict;
struct cache_entry *entry;
void *data;

if (cache->nprec + cache->nprobe + cache->ninflight < cache->cap) {
/* Get an entry from the unused partition. */
unsigned eprobe = cs->gprobe;
unsigned n = cache->ngprobe;
while (n--)
eprobe = cache->ce[eprobe].prev;
evict = &cache->ce[eprobe];
entry = &cache->ce[eprobe];
} else {
/* Get an unused cached entry. */
if (cs->nuprobe != 0 &&
(cs->nuprec == 0 || cache->nprobe + bias > cache->dprobe))
evict = evict_probe(cache, cs);
else
evict = evict_prec(cache, cs);
if (cache->entry_cleanup)
cache->entry_cleanup(cache->cleanup_data, evict);
entry = evict_entry(cache, cs, bias);
}
entry->data = evict->data;
evict->data = NULL;
data = entry->data;
entry->data = NULL;
return data;
}

/** Get a cache entry for a given missed key.
Expand Down Expand Up @@ -325,8 +350,11 @@ get_missed_entry(struct cache *cache, cache_key_t key,
* so entry must be from the unused partition in between.
*/

if (!entry->data)
reinit_entry(cache, entry, cs, 1);
if (!entry->data) {
struct cache_entry *evict = evict_entry(cache, cs, 1);
entry->data = evict->data;
evict->data = NULL;
}

if (cache->split == idx)
cache->split = entry->prev;
Expand All @@ -344,15 +372,12 @@ get_missed_entry(struct cache *cache, cache_key_t key,
* @param cache Cache object.
* @param entry Ghost entry to be reused.
* @param idx Index of @p entry.
* @param cs Cache search info.
* @returns The value of @p entry.
*/
static struct cache_entry *
reuse_ghost_entry(struct cache *cache, struct cache_entry *entry,
unsigned idx, struct cache_search *cs)
unsigned idx)
{
reinit_entry(cache, entry, cs, 0);

if (cache->split == idx)
cache->split = entry->prev;

Expand Down Expand Up @@ -389,8 +414,9 @@ get_ghost_or_missed_entry(struct cache *cache, cache_key_t key,
cache->dprobe -= delta;
else
cache->dprobe = 0;
entry->data = reclaim_data(cache, cs, 0);
--cache->ngprec;
return reuse_ghost_entry(cache, entry, idx, cs);
return reuse_ghost_entry(cache, entry, idx);
}
idx = entry->next;
}
Expand All @@ -409,8 +435,9 @@ get_ghost_or_missed_entry(struct cache *cache, cache_key_t key,
cache->dprobe += delta;
else
cache->dprobe = cache->cap;
entry->data = reclaim_data(cache, cs, 0);
--cache->ngprobe;
return reuse_ghost_entry(cache, entry, idx, cs);
return reuse_ghost_entry(cache, entry, idx);
}
idx = entry->prev;
}
Expand Down Expand Up @@ -457,8 +484,8 @@ cache_get_entry_noref(struct cache *cache, cache_key_t key)
struct cache_entry *entry;
unsigned n, idx;

cs.nuprec = 0;
cs.nuprobe = 0;
cs.nzprec = 0;
cs.nzprobe = 0;

/* Search precious entries */
n = cache->nprec;
Expand All @@ -468,8 +495,8 @@ cache_get_entry_noref(struct cache *cache, cache_key_t key)
if (entry->key == key)
return reuse_cached_entry(cache, entry, idx);
if (entry->refcnt == 0) {
cs.uprec = idx;
++cs.nuprec;
cs.zprec = idx;
++cs.nzprec;
}
idx = entry->next;
}
Expand All @@ -486,8 +513,8 @@ cache_get_entry_noref(struct cache *cache, cache_key_t key)
return reuse_cached_entry(cache, entry, idx);
}
if (entry->refcnt == 0) {
cs.uprobe = idx;
++cs.nuprobe;
cs.zprobe = idx;
++cs.nzprobe;
}
idx = entry->prev;
}
Expand All @@ -496,8 +523,8 @@ cache_get_entry_noref(struct cache *cache, cache_key_t key)
entry = get_inflight_entry(cache, key);

if (!entry) {
unsigned inuse = (cache->nprec - cs.nuprec) +
(cache->nprobe - cs.nuprobe) +
unsigned inuse = (cache->nprec - cs.nzprec) +
(cache->nprobe - cs.nzprobe) +
cache->ninflight;
if (inuse >= cache->cap)
return NULL;
Expand Down
53 changes: 53 additions & 0 deletions src/kdumpfile/test-cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,59 @@ main(int argc, char **argv)
cache_insert(cache, entry);
cache_put_entry(cache, entry);

/*******************************************************************
* Check NULL-pointer dereference reported by Stephen Brennan.
*/

/* Put something onto the probe list. */
entry = cache_get_entry(cache, CACHE_SIZE + 1);
if (!entry) {
fprintf(stderr, "Cannot allocate for probe\n");
return TEST_FAIL;
}
cache_insert(cache, entry);
cache_put_entry(cache, entry);

/* Evict the probe entry to the ghost probe partition. */
for (i = 0; i < CACHE_SIZE; ++i) {
poison_stack();
entry = cache_get_entry(cache, i);
if (!entry) {
fprintf(stderr, "Cannot find cached entry %u\n", i);
return TEST_FAIL;
}
if (!cache_entry_valid(entry))
cache_insert(cache, entry);
cache_put_entry(cache, entry);
}

/* Move an entry into the unused partition. */
entry = cache_get_entry(cache, CACHE_SIZE + 2);
if (!entry) {
fprintf(stderr, "Cannot allocate after reinit\n");
return TEST_FAIL;
}
if (!entry->data) {
fprintf(stderr, "NULL data for entry %u\n", CACHE_SIZE);
return TEST_FAIL;
}
cache_discard(cache, entry);
poison_stack();

/* Reuse the ghost probe entry. */
entry = cache_get_entry(cache, CACHE_SIZE + 1);
if (!entry) {
fprintf(stderr, "Cannot reuse ghost probe entry\n");
return TEST_FAIL;
}
if (!entry->data) {
fprintf(stderr, "NULL data for reused ghost probe entry %u\n",
CACHE_SIZE + 1);
return TEST_FAIL;
}
cache_insert(cache, entry);
cache_put_entry(cache, entry);

cache_free(cache);
return TEST_OK;
}

0 comments on commit bba9820

Please sign in to comment.