diff options
author | dormando <dormando@rydia.net> | 2020-01-31 17:02:26 -0800 |
---|---|---|
committer | dormando <dormando@rydia.net> | 2020-01-31 17:02:26 -0800 |
commit | c0e5a9974558179cbc76efd1271cc34e32c29721 (patch) | |
tree | f1d3ada92f756adbf1f840308b41129751849d5f /slabs.c | |
parent | d0ba152f3d1225dec3a1066761e46ebcdebb4922 (diff) | |
download | memcached-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.c | 2 |
1 files changed, 1 insertions, 1 deletions
@@ -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; |