@@ -1559,10 +1559,13 @@ CacheAllocator<CacheTrait>::findEviction(TierId tid, PoolId pid, ClassId cid) {
1559
1559
config_.evictionSearchTries > searchTries)) {
1560
1560
1561
1561
Item* toRecycle = nullptr ;
1562
+ Item* toRecycleParent = nullptr ;
1562
1563
Item* candidate = nullptr ;
1564
+ WriteHandle headHandle{};
1563
1565
typename NvmCacheT::PutToken token;
1564
1566
1565
- mmContainer.withEvictionIterator ([this , tid, pid, cid, &candidate, &toRecycle,
1567
+ mmContainer.withEvictionIterator ([this , tid, pid, cid, &candidate,
1568
+ &toRecycle, &toRecycleParent, &headHandle,
1566
1569
&searchTries, &mmContainer, &lastTier,
1567
1570
&token](auto && itr) {
1568
1571
if (!itr) {
@@ -1577,12 +1580,48 @@ CacheAllocator<CacheTrait>::findEviction(TierId tid, PoolId pid, ClassId cid) {
1577
1580
++searchTries;
1578
1581
(*stats_.evictionAttempts )[tid][pid][cid].inc ();
1579
1582
1580
- auto * toRecycle_ = itr.get ();
1581
- auto * candidate_ =
1583
+ Item * toRecycle_ = itr.get ();
1584
+ Item* toRecycleParent_ =
1582
1585
toRecycle_->isChainedItem ()
1583
1586
? &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
+ }
1586
1625
if (lastTier) {
1587
1626
// if it's last tier, the item will be evicted
1588
1627
// need to create put token before marking it exclusive
@@ -1594,21 +1633,33 @@ CacheAllocator<CacheTrait>::findEviction(TierId tid, PoolId pid, ClassId cid) {
1594
1633
} else if ( (lastTier && candidate_->markForEviction ()) ||
1595
1634
(!lastTier && candidate_->markMoving ()) ) {
1596
1635
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_;
1597
1644
// markForEviction to make sure no other thead is evicting the item
1598
1645
// nor holding a handle to that item if this is last tier
1599
1646
// since we won't be moving the item to the next tier
1600
1647
toRecycle = toRecycle_;
1601
1648
candidate = candidate_;
1602
-
1603
1649
// Check if parent changed for chained items - if yes, we cannot
1604
1650
// remove the child from the mmContainer as we will not be evicting
1605
1651
// it. We could abort right here, but we need to cleanup in case
1606
1652
// unmarkForEviction() returns 0 - so just go through normal path.
1607
1653
if (!toRecycle_->isChainedItem () ||
1608
1654
&toRecycle->asChainedItem ().getParentItem (compressor_) ==
1609
- candidate)
1655
+ toRecycleParent_) {
1610
1656
mmContainer.remove (itr);
1657
+ }
1611
1658
return ;
1659
+ } else if (headHandle_) {
1660
+ XDCHECK (headHandle_->isMoving ());
1661
+ headHandle_->unmarkMoving ();
1662
+ wakeUpWaiters (*headHandle_,std::move (headHandle_));
1612
1663
}
1613
1664
1614
1665
if (candidate_->hasChainedItem ()) {
@@ -1632,6 +1683,34 @@ CacheAllocator<CacheTrait>::findEviction(TierId tid, PoolId pid, ClassId cid) {
1632
1683
auto evictedToNext = lastTier ? nullptr
1633
1684
: tryEvictToNextMemoryTier (*candidate, false );
1634
1685
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
+ }
1635
1714
// if insertOrReplace was called during move
1636
1715
// then candidate will not be accessible (failed replace during tryEvict)
1637
1716
// - therefore this was why we failed to
@@ -1677,6 +1756,10 @@ CacheAllocator<CacheTrait>::findEviction(TierId tid, PoolId pid, ClassId cid) {
1677
1756
1678
1757
(*stats_.numWritebacks )[tid][pid][cid].inc ();
1679
1758
wakeUpWaiters (*candidate, std::move (evictedToNext));
1759
+ if (headHandle) {
1760
+ headHandle->unmarkMoving ();
1761
+ wakeUpWaiters (*headHandle,std::move (headHandle));
1762
+ }
1680
1763
}
1681
1764
1682
1765
XDCHECK (!candidate->isMarkedForEviction () && !candidate->isMoving ());
@@ -1761,7 +1844,9 @@ CacheAllocator<CacheTrait>::tryEvictToNextMemoryTier(
1761
1844
TierId tid, PoolId pid, Item& item, bool fromBgThread) {
1762
1845
XDCHECK (item.isMoving ());
1763
1846
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
+ }
1765
1850
if (item.isExpired ()) {
1766
1851
accessContainer_->remove (item);
1767
1852
item.unmarkMoving ();
0 commit comments