@@ -1559,10 +1559,13 @@ CacheAllocator<CacheTrait>::findEviction(TierId tid, PoolId pid, ClassId cid) {
15591559 config_.evictionSearchTries > searchTries)) {
15601560
15611561 Item* toRecycle = nullptr ;
1562+ Item* toRecycleParent = nullptr ;
15621563 Item* candidate = nullptr ;
1564+ WriteHandle headHandle{};
15631565 typename NvmCacheT::PutToken token;
15641566
1565- mmContainer.withEvictionIterator ([this , tid, pid, cid, &candidate, &toRecycle,
1567+ mmContainer.withEvictionIterator ([this , tid, pid, cid, &candidate,
1568+ &toRecycle, &toRecycleParent, &headHandle,
15661569 &searchTries, &mmContainer, &lastTier,
15671570 &token](auto && itr) {
15681571 if (!itr) {
@@ -1577,12 +1580,48 @@ CacheAllocator<CacheTrait>::findEviction(TierId tid, PoolId pid, ClassId cid) {
15771580 ++searchTries;
15781581 (*stats_.evictionAttempts )[tid][pid][cid].inc ();
15791582
1580- auto * toRecycle_ = itr.get ();
1581- auto * candidate_ =
1583+ Item * toRecycle_ = itr.get ();
1584+ Item* toRecycleParent_ =
15821585 toRecycle_->isChainedItem ()
15831586 ? &toRecycle_->asChainedItem ().getParentItem (compressor_)
1584- : toRecycle_;
1585-
1587+ : nullptr ;
1588+ WriteHandle headHandle_{};
1589+ WriteHandle parentHandle_{};
1590+ Item* candidate_;
1591+ bool isHead = false ;
1592+ if (!lastTier && toRecycle_->isChainedItem ()) {
1593+ const auto parentKey = toRecycleParent_->getKey ();
1594+ auto lock = chainedItemLocks_.tryLockExclusive (parentKey);
1595+ if (!lock || &toRecycle_->asChainedItem ().getParentItem (compressor_) !=
1596+ toRecycleParent_) {
1597+ ++itr;
1598+ continue ;
1599+ }
1600+ XDCHECK_EQ (&toRecycle_->asChainedItem ().getParentItem (compressor_),
1601+ toRecycleParent_);
1602+ headHandle_ = findChainedItem (*toRecycleParent_);
1603+ if (headHandle_) {
1604+ if (headHandle_.get () == toRecycle_) {
1605+ // we don't need to mark head handle as moving
1606+ headHandle_.reset ();
1607+ isHead = true ;
1608+ } else {
1609+ bool marked = headHandle_->markMoving ();
1610+ if (!marked) {
1611+ ++itr;
1612+ continue ;
1613+ }
1614+ }
1615+ } else {
1616+ ++itr;
1617+ continue ;
1618+ }
1619+ candidate_ = toRecycle_;
1620+ } else if (lastTier && toRecycle_->isChainedItem ()) {
1621+ candidate_ = toRecycleParent_;
1622+ } else {
1623+ candidate_ = toRecycle_;
1624+ }
15861625 if (lastTier) {
15871626 // if it's last tier, the item will be evicted
15881627 // need to create put token before marking it exclusive
@@ -1594,21 +1633,33 @@ CacheAllocator<CacheTrait>::findEviction(TierId tid, PoolId pid, ClassId cid) {
15941633 } else if ( (lastTier && candidate_->markForEviction ()) ||
15951634 (!lastTier && candidate_->markMoving ()) ) {
15961635 XDCHECK (candidate_->isMoving () || candidate_->isMarkedForEviction ());
1636+ if (isHead) {
1637+ XDCHECK (!headHandle);
1638+ }
1639+ if (headHandle_) {
1640+ XDCHECK (headHandle_->isMoving ());
1641+ headHandle = std::move (headHandle_);
1642+ }
1643+ toRecycleParent = toRecycleParent_;
15971644 // markForEviction to make sure no other thead is evicting the item
15981645 // nor holding a handle to that item if this is last tier
15991646 // since we won't be moving the item to the next tier
16001647 toRecycle = toRecycle_;
16011648 candidate = candidate_;
1602-
16031649 // Check if parent changed for chained items - if yes, we cannot
16041650 // remove the child from the mmContainer as we will not be evicting
16051651 // it. We could abort right here, but we need to cleanup in case
16061652 // unmarkForEviction() returns 0 - so just go through normal path.
16071653 if (!toRecycle_->isChainedItem () ||
16081654 &toRecycle->asChainedItem ().getParentItem (compressor_) ==
1609- candidate)
1655+ toRecycleParent_) {
16101656 mmContainer.remove (itr);
1657+ }
16111658 return ;
1659+ } else if (headHandle_) {
1660+ XDCHECK (headHandle_->isMoving ());
1661+ headHandle_->unmarkMoving ();
1662+ wakeUpWaiters (*headHandle_,std::move (headHandle_));
16121663 }
16131664
16141665 if (candidate_->hasChainedItem ()) {
@@ -1632,6 +1683,34 @@ CacheAllocator<CacheTrait>::findEviction(TierId tid, PoolId pid, ClassId cid) {
16321683 auto evictedToNext = lastTier ? nullptr
16331684 : tryEvictToNextMemoryTier (*candidate, false );
16341685 if (!evictedToNext) {
1686+ // failed to move a chained item - so evict the entire chain
1687+ if (candidate->isChainedItem ()) {
1688+ // candidate should be parent now
1689+ if (headHandle) {
1690+ XDCHECK_EQ ( &headHandle->asChainedItem ().getParentItem (compressor_),
1691+ toRecycleParent );
1692+ XDCHECK_EQ ( &candidate->asChainedItem ().getParentItem (compressor_),
1693+ toRecycleParent );
1694+ }
1695+ bool marked = toRecycleParent->markMoving ();
1696+ while (!marked) {
1697+ // we have a candidated marked moving and we failed to mark the parent
1698+ // we can't evict this item since we failed to allocate in next tier
1699+ // we might have to
1700+ marked = toRecycleParent->markMoving ();
1701+ }
1702+ XDCHECK (marked);
1703+ candidate->unmarkMoving (); // old candidate was the child item
1704+ toRecycle = candidate; // we really want to recycle the child
1705+ candidate = toRecycleParent; // but now we evict the chain and in
1706+ // doing so recycle the child
1707+ if (headHandle) {
1708+ XDCHECK_EQ (headHandle->getRefCount (),2 );
1709+ headHandle->unmarkMoving ();
1710+ headHandle.reset ();
1711+ XDCHECK (!headHandle);
1712+ }
1713+ }
16351714 // if insertOrReplace was called during move
16361715 // then candidate will not be accessible (failed replace during tryEvict)
16371716 // - therefore this was why we failed to
@@ -1677,6 +1756,10 @@ CacheAllocator<CacheTrait>::findEviction(TierId tid, PoolId pid, ClassId cid) {
16771756
16781757 (*stats_.numWritebacks )[tid][pid][cid].inc ();
16791758 wakeUpWaiters (*candidate, std::move (evictedToNext));
1759+ if (headHandle) {
1760+ headHandle->unmarkMoving ();
1761+ wakeUpWaiters (*headHandle,std::move (headHandle));
1762+ }
16801763 }
16811764
16821765 XDCHECK (!candidate->isMarkedForEviction () && !candidate->isMoving ());
@@ -1761,7 +1844,9 @@ CacheAllocator<CacheTrait>::tryEvictToNextMemoryTier(
17611844 TierId tid, PoolId pid, Item& item, bool fromBgThread) {
17621845 XDCHECK (item.isMoving ());
17631846 XDCHECK (item.getRefCount () == 0 );
1764- if (item.hasChainedItem ()) return WriteHandle{}; // TODO: We do not support ChainedItem yet
1847+ if (item.isChainedItem () || item.hasChainedItem ()) {
1848+ return WriteHandle{};
1849+ }
17651850 if (item.isExpired ()) {
17661851 accessContainer_->remove (item);
17671852 item.unmarkMoving ();
0 commit comments