summaryrefslogtreecommitdiff
path: root/slabs.c
diff options
context:
space:
mode:
authordormando <dormando@rydia.net>2020-01-31 17:02:26 -0800
committerdormando <dormando@rydia.net>2020-01-31 17:02:26 -0800
commitc0e5a9974558179cbc76efd1271cc34e32c29721 (patch)
treef1d3ada92f756adbf1f840308b41129751849d5f /slabs.c
parentd0ba152f3d1225dec3a1066761e46ebcdebb4922 (diff)
downloadmemcached-c0e5a9974558179cbc76efd1271cc34e32c29721.tar.gz
slabs: fix crash in page mover
PR #524 speeds up the slab page mover for some situations: if an item is expired, but not yet reaped (by a fetch, or LRU crawler), the slab page mover unlinks the item, marks it as free, and doesn't mark the loop as "Busy". If any items were marked as 'busy' (needing to be freed, re-checked, or actively being used) the page mover will re-scan all entries from the top. With this change the re-scan becomes less likely. The bug is in the check for chunked items. `ch` is only set if an ITEM_CHUNK is detected. If ITEM_CHUNKED is instead detected we also need to handle it with the full free routine. This was not done. This frees the header item (CHUNKED), and then leaks all of the chunks associated with it. Getting into this situation is hard: - have an expired chunked item in memory that the LRU crawler hasn't caught. - have the page mover find and move the page specifically with the header item, not an individual chunk (these are in the small slab classes). - later on pages with the leaked chunks get moved to other slab classes. The slab mover thinks they're either free or that it needs to unlink the orphaned chunks. This should then crash in the page mover since the next/prev headers may no longer make sense. Or, worse, this has to go on for a while since the may make sense. This one liner fixes it.
Diffstat (limited to 'slabs.c')
-rw-r--r--slabs.c2
1 files changed, 1 insertions, 1 deletions
diff --git a/slabs.c b/slabs.c
index 2847700..3478921 100644
--- a/slabs.c
+++ b/slabs.c
@@ -1034,7 +1034,7 @@ static int slab_rebalance_move(void) {
/* unlink and mark as done if it's not
* a chunked item as they require more book-keeping) */
STORAGE_delete(storage, it);
- if (!ch) {
+ if (!ch && (it->it_flags & ITEM_CHUNKED) == 0) {
do_item_unlink(it, hv);
it->it_flags = ITEM_SLABBED|ITEM_FETCHED;
it->refcount = 0;