mdbx: add MDBX_PNL_MAX and checking size of lists.

Change-Id: I32e31d2caf676e1e96cc4f82610544b5c5ee3a6d
This commit is contained in:
Leonid Yuriev 2018-08-10 11:28:05 +03:00
parent b4fd29a67b
commit c472300b13
3 changed files with 77 additions and 53 deletions

View File

@ -512,6 +512,7 @@ typedef MDBX_ID2 *MDBX_ID2L;
#define MDBX_PNL_DB_MAX (MDBX_PNL_DB_SIZE - 1)
#define MDBX_PNL_UM_MAX (MDBX_PNL_UM_SIZE - 1)
#define MDBX_PNL_MAX (MAX_PAGENO / 4)
#define MDBX_PNL_SIZEOF(pl) (((pl)[0] + 1) * sizeof(pgno_t))
#define MDBX_PNL_IS_ZERO(pl) ((pl)[0] == 0)

View File

@ -501,6 +501,7 @@ __cold void mdbx_rthc_remove(const mdbx_thread_key_t key) {
* Allocates memory for an PNL of the given size.
* Returns PNL on success, NULL on failure. */
static MDBX_PNL mdbx_pnl_alloc(size_t size) {
assert(size <= MDBX_PNL_MAX);
MDBX_PNL pl = malloc((size + 2) * sizeof(pgno_t));
if (likely(pl)) {
*pl++ = (pgno_t)size;
@ -540,8 +541,11 @@ static __inline void mdbx_pnl_xappend(MDBX_PNL pl, pgno_t id) {
pl[pl[0] += 1] = id;
}
static bool mdbx_pnl_check(MDBX_PNL pl) {
static bool mdbx_pnl_check(MDBX_PNL pl, bool allocated) {
if (pl) {
if (allocated) {
assert(pl[0] <= MDBX_PNL_MAX && pl[0] <= pl[-1]);
}
for (const pgno_t *ptr = pl + pl[0]; --ptr > pl;) {
assert(MDBX_PNL_ORDERED(ptr[0], ptr[1]));
assert(ptr[0] >= NUM_METAS);
@ -555,6 +559,7 @@ static bool mdbx_pnl_check(MDBX_PNL pl) {
/* Sort an PNL.
* [in,out] pnl The PNL to sort. */
static void __hot mdbx_pnl_sort(MDBX_PNL pnl) {
assert(pnl[0] <= MDBX_PNL_MAX && pnl[0] <= pnl[-1]);
/* Max possible depth of int-indexed tree * 2 items/level */
int istack[sizeof(int) * CHAR_BIT * 2];
int i, j, k, l, ir, jstack;
@ -629,7 +634,7 @@ static void __hot mdbx_pnl_sort(MDBX_PNL pnl) {
}
#undef PNL_SMALL
#undef PNL_SWAP
assert(mdbx_pnl_check(pnl));
assert(mdbx_pnl_check(pnl, true));
}
/* Search for an ID in an PNL.
@ -637,7 +642,7 @@ static void __hot mdbx_pnl_sort(MDBX_PNL pnl) {
* [in] id The ID to search for.
* Returns The index of the first ID greater than or equal to id. */
static unsigned __hot mdbx_pnl_search(MDBX_PNL pnl, pgno_t id) {
assert(mdbx_pnl_check(pnl));
assert(mdbx_pnl_check(pnl, true));
/* binary search of id in pl
* if found, returns position of id
@ -687,18 +692,23 @@ static void mdbx_pnl_shrink(MDBX_PNL *ppl) {
/* Grow an PNL.
* Return the PNL to the size growed by given number.
* [in,out] ppl Address of the PNL to grow. */
static int mdbx_pnl_grow(MDBX_PNL *ppl, size_t num) {
static int __must_check_result mdbx_pnl_grow(MDBX_PNL *ppl, size_t num) {
MDBX_PNL idn = *ppl - 1;
assert(idn[0] <= MDBX_PNL_MAX && idn[0] <= idn[-1]);
assert(num <= MDBX_PNL_MAX);
num += *idn;
if (unlikely(num > MDBX_PNL_MAX))
return MDBX_TXN_FULL;
/* grow it */
idn = realloc(idn, (*idn + num + 2) * sizeof(pgno_t));
idn = realloc(idn, (num + 2) * sizeof(pgno_t));
if (unlikely(!idn))
return MDBX_ENOMEM;
*idn++ += (pgno_t)num;
*ppl = idn;
return 0;
return MDBX_SUCCESS;
}
static int mdbx_txl_grow(MDBX_TXL *ptr, size_t num) {
static int __must_check_result mdbx_txl_grow(MDBX_TXL *ptr, size_t num) {
MDBX_TXL list = *ptr - 1;
/* grow it */
list = realloc(list, ((size_t)*list + num + 2) * sizeof(txnid_t));
@ -706,85 +716,96 @@ static int mdbx_txl_grow(MDBX_TXL *ptr, size_t num) {
return MDBX_ENOMEM;
*list++ += num;
*ptr = list;
return 0;
return MDBX_SUCCESS;
}
/* Make room for num additional elements in an PNL.
* [in,out] ppl Address of the PNL.
* [in] num Number of elements to make room for.
* Returns 0 on success, MDBX_ENOMEM on failure. */
static int mdbx_pnl_need(MDBX_PNL *ppl, size_t num) {
static int __must_check_result mdbx_pnl_need(MDBX_PNL *ppl, size_t num) {
MDBX_PNL pl = *ppl;
assert(pl[0] <= MDBX_PNL_MAX && pl[0] <= pl[-1]);
assert(num <= MDBX_PNL_MAX);
num += pl[0];
if (unlikely(num > pl[-1])) {
num = (num + num / 4 + (256 + 2)) & -256;
if (unlikely(num > MDBX_PNL_MAX))
return MDBX_TXN_FULL;
num = (num + num / 4 + (256 + 2)) & ~255u;
num = (num < MDBX_PNL_MAX + 2) ? num : MDBX_PNL_MAX + 2;
pl = realloc(pl - 1, num * sizeof(pgno_t));
if (unlikely(!pl))
return MDBX_ENOMEM;
*pl++ = (pgno_t)num - 2;
*ppl = pl;
}
return 0;
return MDBX_SUCCESS;
}
/* Append an ID onto an PNL.
* [in,out] ppl Address of the PNL to append to.
* [in] id The ID to append.
* Returns 0 on success, MDBX_ENOMEM if the PNL is too large. */
static int mdbx_pnl_append(MDBX_PNL *ppl, pgno_t id) {
static int __must_check_result mdbx_pnl_append(MDBX_PNL *ppl, pgno_t id) {
MDBX_PNL pl = *ppl;
/* Too big? */
if (unlikely(pl[0] >= pl[-1])) {
if (mdbx_pnl_grow(ppl, MDBX_PNL_UM_MAX))
return MDBX_ENOMEM;
int rc = mdbx_pnl_grow(ppl, MDBX_PNL_UM_MAX);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
pl = *ppl;
}
pl[0]++;
pl[pl[0]] = id;
return 0;
return MDBX_SUCCESS;
}
static int mdbx_txl_append(MDBX_TXL *ptr, txnid_t id) {
static int __must_check_result mdbx_txl_append(MDBX_TXL *ptr, txnid_t id) {
MDBX_TXL list = *ptr;
/* Too big? */
if (unlikely(list[0] >= list[-1])) {
if (mdbx_txl_grow(ptr, (size_t)list[0]))
return MDBX_ENOMEM;
int rc = mdbx_txl_grow(ptr, (size_t)list[0]);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
list = *ptr;
}
list[0]++;
list[list[0]] = id;
return 0;
return MDBX_SUCCESS;
}
/* Append an PNL onto an PNL.
* [in,out] ppl Address of the PNL to append to.
* [in] app The PNL to append.
* Returns 0 on success, MDBX_ENOMEM if the PNL is too large. */
static int mdbx_pnl_append_list(MDBX_PNL *ppl, MDBX_PNL app) {
static int __must_check_result mdbx_pnl_append_list(MDBX_PNL *ppl,
MDBX_PNL app) {
MDBX_PNL pnl = *ppl;
/* Too big? */
if (unlikely(pnl[0] + app[0] >= pnl[-1])) {
if (mdbx_pnl_grow(ppl, app[0]))
return MDBX_ENOMEM;
int rc = mdbx_pnl_grow(ppl, app[0]);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
pnl = *ppl;
}
memcpy(&pnl[pnl[0] + 1], &app[1], app[0] * sizeof(pgno_t));
pnl[0] += app[0];
return 0;
return MDBX_SUCCESS;
}
static int mdbx_txl_append_list(MDBX_TXL *ptr, MDBX_TXL append) {
static int __must_check_result mdbx_txl_append_list(MDBX_TXL *ptr,
MDBX_TXL append) {
MDBX_TXL list = *ptr;
/* Too big? */
if (unlikely(list[0] + append[0] >= list[-1])) {
if (mdbx_txl_grow(ptr, (size_t)append[0]))
return MDBX_ENOMEM;
int rc = mdbx_txl_grow(ptr, (size_t)append[0]);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
list = *ptr;
}
memcpy(&list[list[0] + 1], &append[1], (size_t)append[0] * sizeof(txnid_t));
list[0] += append[0];
return 0;
return MDBX_SUCCESS;
}
/* Append an ID range onto an PNL.
@ -792,27 +813,29 @@ static int mdbx_txl_append_list(MDBX_TXL *ptr, MDBX_TXL append) {
* [in] id The lowest ID to append.
* [in] n Number of IDs to append.
* Returns 0 on success, MDBX_ENOMEM if the PNL is too large. */
static int mdbx_pnl_append_range(MDBX_PNL *ppl, pgno_t id, size_t n) {
static int __must_check_result mdbx_pnl_append_range(MDBX_PNL *ppl, pgno_t id,
size_t n) {
pgno_t *pnl = *ppl, len = pnl[0];
/* Too big? */
if (unlikely(len + n > pnl[-1])) {
if (mdbx_pnl_grow(ppl, n | MDBX_PNL_UM_MAX))
return MDBX_ENOMEM;
int rc = mdbx_pnl_grow(ppl, n | MDBX_PNL_UM_MAX);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
pnl = *ppl;
}
pnl[0] = len + (pgno_t)n;
pnl += len;
while (n)
pnl[n--] = id++;
return 0;
return MDBX_SUCCESS;
}
/* Merge an PNL onto an PNL. The destination PNL must be big enough.
* [in] pl The PNL to merge into.
* [in] merge The PNL to merge. */
static void __hot mdbx_pnl_xmerge(MDBX_PNL pnl, MDBX_PNL merge) {
assert(mdbx_pnl_check(pnl));
assert(mdbx_pnl_check(merge));
assert(mdbx_pnl_check(pnl, true));
assert(mdbx_pnl_check(merge, false));
pgno_t old_id, merge_id, i = merge[0], j = pnl[0], k = i + j, total = k;
pnl[0] =
MDBX_PNL_ASCENDING ? 0 : ~(pgno_t)0; /* delimiter for pl scan below */
@ -824,7 +847,7 @@ static void __hot mdbx_pnl_xmerge(MDBX_PNL pnl, MDBX_PNL merge) {
pnl[k--] = merge_id;
}
pnl[0] = total;
assert(mdbx_pnl_check(pnl));
assert(mdbx_pnl_check(pnl, true));
}
/* Search for an ID in an ID2L.
@ -2171,7 +2194,7 @@ static int mdbx_page_alloc(MDBX_cursor *mc, unsigned num, MDBX_page **mp,
}
}
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist));
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true));
pgno_t pgno, *repg_list = env->me_reclaimed_pglist;
unsigned repg_pos = 0, repg_len = repg_list ? repg_list[0] : 0;
txnid_t oldest = 0, last = 0;
@ -2191,7 +2214,7 @@ static int mdbx_page_alloc(MDBX_cursor *mc, unsigned num, MDBX_page **mp,
/* Seek a big enough contiguous page range.
* Prefer pages with lower pgno. */
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist));
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true));
if (likely(flags & MDBX_ALLOC_CACHE) && repg_len > wanna_range &&
(!(flags & MDBX_COALESCE) || op == MDBX_FIRST)) {
#if MDBX_PNL_ASCENDING
@ -2305,7 +2328,7 @@ static int mdbx_page_alloc(MDBX_cursor *mc, unsigned num, MDBX_page **mp,
pgno_t *re_pnl = (pgno_t *)data.iov_base;
mdbx_tassert(txn, re_pnl[0] == 0 ||
data.iov_len == (re_pnl[0] + 1) * sizeof(pgno_t));
mdbx_tassert(txn, mdbx_pnl_check(re_pnl));
mdbx_tassert(txn, mdbx_pnl_check(re_pnl, false));
repg_pos = re_pnl[0];
if (!repg_list) {
if (unlikely(!(env->me_reclaimed_pglist = repg_list =
@ -2375,7 +2398,7 @@ static int mdbx_page_alloc(MDBX_cursor *mc, unsigned num, MDBX_page **mp,
mdbx_info("refunded %" PRIaPGNO " pages: %" PRIaPGNO " -> %" PRIaPGNO,
tail - txn->mt_next_pgno, tail, txn->mt_next_pgno);
txn->mt_next_pgno = tail;
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist));
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true));
}
}
@ -2485,7 +2508,7 @@ static int mdbx_page_alloc(MDBX_cursor *mc, unsigned num, MDBX_page **mp,
}
fail:
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist));
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true));
if (mp) {
*mp = NULL;
txn->mt_flags |= MDBX_TXN_ERROR;
@ -2516,7 +2539,7 @@ done:
repg_list[0] = repg_len -= num;
for (unsigned i = repg_pos - num; i < repg_len;)
repg_list[++i] = repg_list[++repg_pos];
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist));
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true));
} else {
txn->mt_next_pgno = pgno + num;
mdbx_assert(env, txn->mt_next_pgno <= txn->mt_end_pgno);
@ -2533,7 +2556,7 @@ done:
mdbx_page_dirty(txn, np);
*mp = np;
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist));
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true));
return MDBX_SUCCESS;
}
@ -3510,14 +3533,14 @@ static int mdbx_freelist_save(MDBX_txn *txn) {
(env->me_flags & (MDBX_NOMEMINIT | MDBX_WRITEMAP)) ? SSIZE_MAX
: env->me_maxfree_1pg;
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist));
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true));
again_on_freelist_change:
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist));
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true));
while (1) {
/* Come back here after each Put() in case freelist changed */
MDBX_val key, data;
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist));
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true));
if (!lifo) {
/* If using records from freeDB which we have not yet deleted,
* now delete them and any we reserved for me_reclaimed_pglist. */
@ -3560,7 +3583,7 @@ again_on_freelist_change:
}
}
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist));
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true));
if (txn->mt_loose_pages) {
/* Return loose page numbers to me_reclaimed_pglist,
* though usually none are left at this point.
@ -3613,7 +3636,7 @@ again_on_freelist_change:
txn->mt_loose_count = 0;
}
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist));
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true));
if (env->me_reclaimed_pglist) {
/* Refund suitable pages into "unallocated" space */
pgno_t tail = txn->mt_next_pgno;
@ -3641,7 +3664,7 @@ again_on_freelist_change:
mdbx_info("refunded %" PRIaPGNO " pages: %" PRIaPGNO " -> %" PRIaPGNO,
tail - txn->mt_next_pgno, tail, txn->mt_next_pgno);
txn->mt_next_pgno = tail;
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist));
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true));
}
}
@ -3682,7 +3705,7 @@ again_on_freelist_change:
continue;
}
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist));
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true));
const intptr_t rpl_len =
(env->me_reclaimed_pglist ? env->me_reclaimed_pglist[0] : 0) +
txn->mt_loose_count;
@ -3760,7 +3783,7 @@ again_on_freelist_change:
key.iov_base = &head_id;
data.iov_len = (head_room + 1) * sizeof(pgno_t);
rc = mdbx_cursor_put(&mc, &key, &data, MDBX_RESERVE);
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist));
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true));
if (unlikely(rc))
goto bailout;
@ -3780,7 +3803,7 @@ again_on_freelist_change:
/* Fill in the reserved me_reclaimed_pglist records */
rc = MDBX_SUCCESS;
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist));
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true));
if (env->me_reclaimed_pglist && env->me_reclaimed_pglist[0]) {
MDBX_val key, data;
key.iov_len = data.iov_len = 0; /* avoid MSVC warning */
@ -3831,11 +3854,11 @@ again_on_freelist_change:
data.iov_base = rpl_end;
pgno_t save = rpl_end[0];
rpl_end[0] = (pgno_t)chunk_len;
mdbx_tassert(txn, mdbx_pnl_check(rpl_end));
mdbx_tassert(txn, mdbx_pnl_check(rpl_end, false));
mc.mc_flags |= C_RECLAIMING;
rc = mdbx_cursor_put(&mc, &key, &data, MDBX_CURRENT);
mc.mc_flags ^= C_RECLAIMING;
mdbx_tassert(txn, mdbx_pnl_check(rpl_end));
mdbx_tassert(txn, mdbx_pnl_check(rpl_end, false));
mdbx_tassert(
txn, cleanup_reclaimed_pos ==
(txn->mt_lifo_reclaimed ? txn->mt_lifo_reclaimed[0] : 0));

View File

@ -340,7 +340,7 @@ static int handle_freedb(const uint64_t record_number, const MDBX_val *key,
data->iov_len);
else {
const pgno_t number = *iptr++;
if (number >= MDBX_PNL_UM_MAX)
if (number < 1 || number > MDBX_PNL_MAX)
problem_add("entry", record_number, "wrong idl length", "%" PRIiPTR "",
number);
else if ((number + 1) * sizeof(pgno_t) != data->iov_len)