/* * __wt_rec_track_wrapup_err -- * Resolve the page's list of tracked objects after an error occurs. */ int __wt_rec_track_wrapup_err(WT_SESSION_IMPL *session, WT_PAGE *page) { WT_BM *bm; WT_DECL_RET; WT_PAGE_MODIFY *mod; WT_PAGE_TRACK *track; uint32_t i; bm = session->btree->bm; /* * After a failed reconciliation of a page, discard entries added in the * current reconciliation, their information is incorrect, additionally, * clear the in-use flag in preparation for the next reconciliation. */ mod = page->modify; for (track = mod->track, i = 0; i < mod->track_entries; ++track, ++i) if (F_ISSET(track, WT_TRK_JUST_ADDED)) { /* * The in-use flag is used to avoid discarding backing * blocks: if an object is both just-added and in-use, * we allocated the blocks on this run, and we want to * discard them on error. */ if (F_ISSET(track, WT_TRK_INUSE)) WT_TRET(bm->free(bm, session, track->addr.addr, track->addr.size)); __wt_free(session, track->addr.addr); memset(track, 0, sizeof(*track)); } else F_CLR(track, WT_TRK_INUSE); return (ret); }
/* * __ovfl_reuse_wrapup_err -- * Resolve the page's overflow reuse list after an error occurs. */ static int __ovfl_reuse_wrapup_err(WT_SESSION_IMPL *session, WT_PAGE *page) { WT_BM *bm; WT_DECL_RET; WT_OVFL_REUSE **e, **head, *reuse; size_t decr; int i; bm = S2BT(session)->bm; head = page->modify->ovfl_track->ovfl_reuse; /* * Discard any overflow records that were just added, freeing underlying * blocks. * * First, walk the overflow reuse lists (except for the lowest one), * fixing up skiplist links. */ for (i = WT_SKIP_MAXDEPTH - 1; i > 0; --i) for (e = &head[i]; (reuse = *e) != NULL;) { if (!F_ISSET(reuse, WT_OVFL_REUSE_JUST_ADDED)) { e = &reuse->next[i]; continue; } *e = reuse->next[i]; } /* * Second, discard any overflow record with a just-added flag, clear the * flags for the next run. */ decr = 0; for (e = &head[0]; (reuse = *e) != NULL;) { if (!F_ISSET(reuse, WT_OVFL_REUSE_JUST_ADDED)) { F_CLR(reuse, WT_OVFL_REUSE_INUSE); e = &reuse->next[0]; continue; } *e = reuse->next[0]; if (WT_VERBOSE_ISSET(session, WT_VERB_OVERFLOW)) WT_RET( __ovfl_reuse_verbose(session, page, reuse, "free")); WT_TRET(bm->free( bm, session, WT_OVFL_REUSE_ADDR(reuse), reuse->addr_size)); decr += WT_OVFL_SIZE(reuse, WT_OVFL_REUSE); __wt_free(session, reuse); } if (decr != 0) __wt_cache_page_inmem_decr(session, page, decr); return (0); }
/* * __wt_ovfl_discard -- * Discard an on-page overflow value, and reset the page's cell. */ int __wt_ovfl_discard(WT_SESSION_IMPL *session, WT_CELL *cell) { WT_BM *bm; WT_BTREE *btree; WT_CELL_UNPACK *unpack, _unpack; WT_DECL_RET; btree = S2BT(session); bm = btree->bm; unpack = &_unpack; __wt_cell_unpack(cell, unpack); /* * Finally remove overflow key/value objects, called when reconciliation * finishes after successfully writing a page. * * Keys must have already been instantiated and value objects must have * already been cached (if they might potentially still be read by any * running transaction). * * Acquire the overflow lock to avoid racing with a thread reading the * backing overflow blocks. */ WT_RET(__wt_writelock(session, btree->ovfl_lock)); switch (unpack->raw) { case WT_CELL_KEY_OVFL: __wt_cell_type_reset(session, unpack->cell, WT_CELL_KEY_OVFL, WT_CELL_KEY_OVFL_RM); break; case WT_CELL_VALUE_OVFL: __wt_cell_type_reset(session, unpack->cell, WT_CELL_VALUE_OVFL, WT_CELL_VALUE_OVFL_RM); break; WT_ILLEGAL_VALUE(session); } WT_TRET(__wt_writeunlock(session, btree->ovfl_lock)); /* Free the backing disk blocks. */ WT_TRET(bm->free(bm, session, unpack->data, unpack->size)); return (ret); }
/* * __ovfl_reuse_wrapup -- * Resolve the page's overflow reuse list after a page is written. */ static int __ovfl_reuse_wrapup(WT_SESSION_IMPL *session, WT_PAGE *page) { WT_BM *bm; WT_OVFL_REUSE **e, **head, *reuse; size_t decr; int i; bm = S2BT(session)->bm; head = page->modify->ovfl_track->ovfl_reuse; /* * Discard any overflow records that aren't in-use, freeing underlying * blocks. * * First, walk the overflow reuse lists (except for the lowest one), * fixing up skiplist links. */ for (i = WT_SKIP_MAXDEPTH - 1; i > 0; --i) for (e = &head[i]; (reuse = *e) != NULL;) { if (F_ISSET(reuse, WT_OVFL_REUSE_INUSE)) { e = &reuse->next[i]; continue; } *e = reuse->next[i]; } /* * Second, discard any overflow record without an in-use flag, clear * the flags for the next run. * * As part of the pass through the lowest level, figure out how much * space we added/subtracted from the page, and update its footprint. * We don't get it exactly correct because we don't know the depth of * the skiplist here, but it's close enough, and figuring out the * memory footprint change in the reconciliation wrapup code means * fewer atomic updates and less code overall. */ decr = 0; for (e = &head[0]; (reuse = *e) != NULL;) { if (F_ISSET(reuse, WT_OVFL_REUSE_INUSE)) { F_CLR(reuse, WT_OVFL_REUSE_INUSE | WT_OVFL_REUSE_JUST_ADDED); e = &reuse->next[0]; continue; } *e = reuse->next[0]; WT_ASSERT(session, !F_ISSET(reuse, WT_OVFL_REUSE_JUST_ADDED)); if (WT_VERBOSE_ISSET(session, WT_VERB_OVERFLOW)) WT_RET( __ovfl_reuse_verbose(session, page, reuse, "free")); WT_RET(bm->free( bm, session, WT_OVFL_REUSE_ADDR(reuse), reuse->addr_size)); decr += WT_OVFL_SIZE(reuse, WT_OVFL_REUSE); __wt_free(session, reuse); } if (decr != 0) __wt_cache_page_inmem_decr(session, page, decr); return (0); }
/* * __wt_rec_track_wrapup -- * Resolve the page's list of tracked objects after the page is written. */ int __wt_rec_track_wrapup(WT_SESSION_IMPL *session, WT_PAGE *page) { WT_BM *bm; WT_PAGE_MODIFY *mod; WT_PAGE_TRACK *track; uint32_t i; bm = session->btree->bm; if (WT_VERBOSE_ISSET(session, reconcile)) WT_RET(__track_dump(session, page, "reconcile wrapup")); /* * After the successful reconciliation of a page, some of the objects * we're tracking are no longer needed, free what we can free. */ mod = page->modify; for (track = mod->track, i = 0; i < mod->track_entries; ++track, ++i) { /* Ignore empty slots */ if (!F_ISSET(track, WT_TRK_OBJECT)) continue; /* Ignore cached overflow values. */ if (F_ISSET(track, WT_TRK_OVFL_VALUE)) continue; /* * Ignore discarded objects (discarded objects left on the list * are never just-added, never in-use, and only include objects * found on a page). */ if (F_ISSET(track, WT_TRK_DISCARD)) { WT_ASSERT(session, !F_ISSET(track, WT_TRK_JUST_ADDED | WT_TRK_INUSE)); WT_ASSERT(session, F_ISSET(track, WT_TRK_ONPAGE)); continue; } /* Clear the just-added flag, reconciliation succeeded. */ F_CLR(track, WT_TRK_JUST_ADDED); /* * Ignore in-use objects, other than to clear the in-use flag * in preparation for the next reconciliation. */ if (F_ISSET(track, WT_TRK_INUSE)) { F_CLR(track, WT_TRK_INUSE); continue; } /* * The object isn't in-use and hasn't yet been discarded. We * no longer need the underlying blocks, discard them. */ if (WT_VERBOSE_ISSET(session, reconcile)) WT_RET(__track_msg(session, page, "discard", track)); WT_RET( bm->free(bm, session, track->addr.addr, track->addr.size)); /* * There are page and overflow blocks we track anew as part of * each page reconciliation, we need to know about them even if * the underlying blocks are no longer in use. If the object * came from a page, keep it around. Regardless, only discard * objects once. */ if (F_ISSET(track, WT_TRK_ONPAGE)) { F_SET(track, WT_TRK_DISCARD); continue; } __wt_free(session, track->addr.addr); memset(track, 0, sizeof(*track)); } return (0); }