@@ -1240,6 +1240,19 @@ bool CacheAllocator<CacheTrait>::moveChainedItem(ChainedItem& oldItem,
12401240  return  true ;
12411241}
12421242
1243+ template  <typename  CacheTrait>
1244+ void  CacheAllocator<CacheTrait>::unlinkItemForEviction(Item& it) {
1245+   XDCHECK (it.isMarkedForEviction ());
1246+   XDCHECK (it.getRefCount () == 0 );
1247+   accessContainer_->remove (it);
1248+   removeFromMMContainer (it);
1249+ 
1250+   //  Since we managed to mark the item for eviction we must be the only
1251+   //  owner of the item.
1252+   const  auto  ref = it.unmarkForEviction ();
1253+   XDCHECK (ref == 0u );
1254+ }
1255+ 
12431256template  <typename  CacheTrait>
12441257typename  CacheAllocator<CacheTrait>::Item*
12451258CacheAllocator<CacheTrait>::findEviction(PoolId pid, ClassId cid) {
@@ -1248,76 +1261,102 @@ CacheAllocator<CacheTrait>::findEviction(PoolId pid, ClassId cid) {
12481261  //  Keep searching for a candidate until we were able to evict it
12491262  //  or until the search limit has been exhausted
12501263  unsigned  int  searchTries = 0 ;
1251-   auto  itr = mmContainer.getEvictionIterator ();
1252-   while  ((config_.evictionSearchTries  == 0  ||
1253-           config_.evictionSearchTries  > searchTries) &&
1254-          itr) {
1255-     ++searchTries;
1256-     (*stats_.evictionAttempts )[pid][cid].inc ();
1257- 
1258-     Item* toRecycle = itr.get ();
1259- 
1260-     Item* candidate =
1261-         toRecycle->isChainedItem ()
1262-             ? &toRecycle->asChainedItem ().getParentItem (compressor_)
1263-             : toRecycle;
1264- 
1265-     //  make sure no other thead is evicting the item
1266-     if  (candidate->getRefCount () != 0  || !candidate->markMoving ()) {
1267-       ++itr;
1268-       continue ;
1269-     }
1270- 
1271-     //  for chained items, the ownership of the parent can change. We try to
1272-     //  evict what we think as parent and see if the eviction of parent
1273-     //  recycles the child we intend to.
1274-     bool  evictionSuccessful = false ;
1275-     {
1276-       auto  toReleaseHandle =
1277-           itr->isChainedItem ()
1278-               ? advanceIteratorAndTryEvictChainedItem (itr)
1279-               : advanceIteratorAndTryEvictRegularItem (mmContainer, itr);
1280-       evictionSuccessful = toReleaseHandle != nullptr ;
1281-       //  destroy toReleaseHandle. The item won't be released to allocator
1282-       //  since we marked for eviction.
1283-     }
1284- 
1285-     const  auto  ref = candidate->unmarkMoving ();
1286-     if  (ref == 0u ) {
1287-       //  Invalidate iterator since later on we may use this mmContainer
1288-       //  again, which cannot be done unless we drop this iterator
1289-       itr.destroy ();
1290- 
1291-       //  recycle the item. it's safe to do so, even if toReleaseHandle was
1292-       //  NULL. If `ref` == 0 then it means that we are the last holder of
1293-       //  that item.
1294-       if  (candidate->hasChainedItem ()) {
1295-         (*stats_.chainedItemEvictions )[pid][cid].inc ();
1296-       } else  {
1297-         (*stats_.regularItemEvictions )[pid][cid].inc ();
1264+   while  (config_.evictionSearchTries  == 0  ||
1265+          config_.evictionSearchTries  > searchTries) {
1266+     Item* toRecycle = nullptr ;
1267+     Item* candidate = nullptr ;
1268+     typename  NvmCacheT::PutToken token;
1269+ 
1270+     mmContainer.withEvictionIterator ([this , pid, cid, &candidate, &toRecycle,
1271+                                       &searchTries, &mmContainer,
1272+                                       &token](auto && itr) {
1273+       if  (!itr) {
1274+         ++searchTries;
1275+         (*stats_.evictionAttempts )[pid][cid].inc ();
1276+         return ;
12981277      }
12991278
1300-       if  (auto  eventTracker = getEventTracker ()) {
1301-         eventTracker->record (AllocatorApiEvent::DRAM_EVICT, candidate->getKey (),
1302-                              AllocatorApiResult::EVICTED, candidate->getSize (),
1303-                              candidate->getConfiguredTTL ().count ());
1304-       }
1279+       while  ((config_.evictionSearchTries  == 0  ||
1280+               config_.evictionSearchTries  > searchTries) &&
1281+              itr) {
1282+         ++searchTries;
1283+         (*stats_.evictionAttempts )[pid][cid].inc ();
1284+ 
1285+         auto * toRecycle_ = itr.get ();
1286+         auto * candidate_ =
1287+             toRecycle_->isChainedItem ()
1288+                 ? &toRecycle_->asChainedItem ().getParentItem (compressor_)
1289+                 : toRecycle_;
1290+ 
1291+         const  bool  evictToNvmCache = shouldWriteToNvmCache (*candidate_);
1292+         if  (evictToNvmCache)
1293+           token = nvmCache_->createPutToken (candidate_->getKey ());
1294+ 
1295+         if  (evictToNvmCache && !token.isValid ()) {
1296+           stats_.evictFailConcurrentFill .inc ();
1297+         } else  if  (candidate_->markForEviction ()) {
1298+           XDCHECK (candidate_->isMarkedForEviction ());
1299+           //  markForEviction to make sure no other thead is evicting the item
1300+           //  nor holding a handle to that item
1301+           toRecycle = toRecycle_;
1302+           candidate = candidate_;
1303+ 
1304+           //  Check if parent changed for chained items - if yes, we cannot
1305+           //  remove the child from the mmContainer as we will not be evicting
1306+           //  it. We could abort right here, but we need to cleanup in case
1307+           //  unmarkForEviction() returns 0 - so just go through normal path.
1308+           if  (!toRecycle_->isChainedItem () ||
1309+               &toRecycle->asChainedItem ().getParentItem (compressor_) ==
1310+                   candidate)
1311+             mmContainer.remove (itr);
1312+           return ;
1313+         } else  {
1314+           if  (candidate_->hasChainedItem ()) {
1315+             stats_.evictFailParentAC .inc ();
1316+           } else  {
1317+             stats_.evictFailAC .inc ();
1318+           }
1319+         }
13051320
1306-       //  check if by releasing the item we intend to, we actually
1307-       //  recycle the candidate.
1308-       if  (ReleaseRes::kRecycled  ==
1309-           releaseBackToAllocator (*candidate, RemoveContext::kEviction ,
1310-                                  /*  isNascent */ false , toRecycle)) {
1311-         return  toRecycle;
1321+         ++itr;
1322+         XDCHECK (toRecycle == nullptr );
1323+         XDCHECK (candidate == nullptr );
13121324      }
1325+     });
1326+ 
1327+     if  (!toRecycle)
1328+       continue ;
1329+ 
1330+     XDCHECK (toRecycle);
1331+     XDCHECK (candidate);
1332+ 
1333+     unlinkItemForEviction (*candidate);
1334+ 
1335+     if  (token.isValid () && shouldWriteToNvmCacheExclusive (*candidate)) {
1336+       nvmCache_->put (*candidate, std::move (token));
1337+     }
1338+ 
1339+     //  recycle the item. it's safe to do so, even if toReleaseHandle was
1340+     //  NULL. If `ref` == 0 then it means that we are the last holder of
1341+     //  that item.
1342+     if  (candidate->hasChainedItem ()) {
1343+       (*stats_.chainedItemEvictions )[pid][cid].inc ();
13131344    } else  {
1314-       XDCHECK (!evictionSuccessful);
1345+       (*stats_.regularItemEvictions )[pid][cid].inc ();
1346+     }
1347+ 
1348+     if  (auto  eventTracker = getEventTracker ()) {
1349+       eventTracker->record (AllocatorApiEvent::DRAM_EVICT, candidate->getKey (),
1350+                            AllocatorApiResult::EVICTED, candidate->getSize (),
1351+                            candidate->getConfiguredTTL ().count ());
13151352    }
13161353
1317-     //  If we destroyed the itr to possibly evict and failed, we restart
1318-     //  from the beginning again
1319-     if  (!itr) {
1320-       itr.resetToBegin ();
1354+     //  check if by releasing the item we intend to, we actually
1355+     //  recycle the candidate.
1356+     auto  ret = releaseBackToAllocator (*candidate, RemoveContext::kEviction ,
1357+                                       /*  isNascent */ false , toRecycle);
1358+     if  (ret == ReleaseRes::kRecycled ) {
1359+       return  toRecycle;
13211360    }
13221361  }
13231362  return  nullptr ;
@@ -2660,126 +2699,6 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
26602699  }
26612700}
26622701
2663- template  <typename  CacheTrait>
2664- typename  CacheAllocator<CacheTrait>::WriteHandle
2665- CacheAllocator<CacheTrait>::advanceIteratorAndTryEvictRegularItem(
2666-     MMContainer& mmContainer, EvictionIterator& itr) {
2667-   //  we should flush this to nvmcache if it is not already present in nvmcache
2668-   //  and the item is not expired.
2669-   Item& item = *itr;
2670-   const  bool  evictToNvmCache = shouldWriteToNvmCache (item);
2671- 
2672-   auto  token = evictToNvmCache ? nvmCache_->createPutToken (item.getKey ())
2673-                                : typename  NvmCacheT::PutToken{};
2674- 
2675-   //  record the in-flight eviciton. If not, we move on to next item to avoid
2676-   //  stalling eviction.
2677-   if  (evictToNvmCache && !token.isValid ()) {
2678-     ++itr;
2679-     stats_.evictFailConcurrentFill .inc ();
2680-     return  WriteHandle{};
2681-   }
2682- 
2683-   //  If there are other accessors, we should abort. Acquire a handle here since
2684-   //  if we remove the item from both access containers and mm containers
2685-   //  below, we will need a handle to ensure proper cleanup in case we end up
2686-   //  not evicting this item
2687-   auto  evictHandle = accessContainer_->removeIf (item, &itemExclusivePredicate);
2688-   if  (!evictHandle) {
2689-     ++itr;
2690-     stats_.evictFailAC .inc ();
2691-     return  evictHandle;
2692-   }
2693- 
2694-   mmContainer.remove (itr);
2695-   XDCHECK_EQ (reinterpret_cast <uintptr_t >(evictHandle.get ()),
2696-              reinterpret_cast <uintptr_t >(&item));
2697-   XDCHECK (!evictHandle->isInMMContainer ());
2698-   XDCHECK (!evictHandle->isAccessible ());
2699- 
2700-   //  Invalidate iterator since later on if we are not evicting this
2701-   //  item, we may need to rely on the handle we created above to ensure
2702-   //  proper cleanup if the item's raw refcount has dropped to 0.
2703-   //  And since this item may be a parent item that has some child items
2704-   //  in this very same mmContainer, we need to make sure we drop this
2705-   //  exclusive iterator so we can gain access to it when we're cleaning
2706-   //  up the child items
2707-   itr.destroy ();
2708- 
2709-   //  Ensure that there are no accessors after removing from the access
2710-   //  container
2711-   XDCHECK (evictHandle->getRefCount () == 1 );
2712- 
2713-   if  (evictToNvmCache && shouldWriteToNvmCacheExclusive (item)) {
2714-     XDCHECK (token.isValid ());
2715-     nvmCache_->put (*evictHandle, std::move (token));
2716-   }
2717-   return  evictHandle;
2718- }
2719- 
2720- template  <typename  CacheTrait>
2721- typename  CacheAllocator<CacheTrait>::WriteHandle
2722- CacheAllocator<CacheTrait>::advanceIteratorAndTryEvictChainedItem(
2723-     EvictionIterator& itr) {
2724-   XDCHECK (itr->isChainedItem ());
2725- 
2726-   ChainedItem* candidate = &itr->asChainedItem ();
2727-   ++itr;
2728- 
2729-   //  The parent could change at any point through transferChain. However, if
2730-   //  that happens, we would realize that the releaseBackToAllocator return
2731-   //  kNotRecycled and we would try another chained item, leading to transient
2732-   //  failure.
2733-   auto & parent = candidate->getParentItem (compressor_);
2734- 
2735-   const  bool  evictToNvmCache = shouldWriteToNvmCache (parent);
2736- 
2737-   auto  token = evictToNvmCache ? nvmCache_->createPutToken (parent.getKey ())
2738-                                : typename  NvmCacheT::PutToken{};
2739- 
2740-   //  if token is invalid, return. iterator is already advanced.
2741-   if  (evictToNvmCache && !token.isValid ()) {
2742-     stats_.evictFailConcurrentFill .inc ();
2743-     return  WriteHandle{};
2744-   }
2745- 
2746-   //  check if the parent exists in the hashtable and refcount is drained.
2747-   auto  parentHandle =
2748-       accessContainer_->removeIf (parent, &itemExclusivePredicate);
2749-   if  (!parentHandle) {
2750-     stats_.evictFailParentAC .inc ();
2751-     return  parentHandle;
2752-   }
2753- 
2754-   //  Invalidate iterator since later on we may use the mmContainer
2755-   //  associated with this iterator which cannot be done unless we
2756-   //  drop this iterator
2757-   // 
2758-   //  This must be done once we know the parent is not nullptr.
2759-   //  Since we can very well be the last holder of this parent item,
2760-   //  which may have a chained item that is linked in this MM container.
2761-   itr.destroy ();
2762- 
2763-   //  Ensure we have the correct parent and we're the only user of the
2764-   //  parent, then free it from access container. Otherwise, we abort
2765-   XDCHECK_EQ (reinterpret_cast <uintptr_t >(&parent),
2766-              reinterpret_cast <uintptr_t >(parentHandle.get ()));
2767-   XDCHECK_EQ (1u , parent.getRefCount ());
2768- 
2769-   removeFromMMContainer (*parentHandle);
2770- 
2771-   XDCHECK (!parent.isInMMContainer ());
2772-   XDCHECK (!parent.isAccessible ());
2773- 
2774-   if  (evictToNvmCache && shouldWriteToNvmCacheExclusive (*parentHandle)) {
2775-     XDCHECK (token.isValid ());
2776-     XDCHECK (parentHandle->hasChainedItem ());
2777-     nvmCache_->put (*parentHandle, std::move (token));
2778-   }
2779- 
2780-   return  parentHandle;
2781- }
2782- 
27832702template  <typename  CacheTrait>
27842703typename  CacheAllocator<CacheTrait>::WriteHandle
27852704CacheAllocator<CacheTrait>::evictNormalItemForSlabRelease(Item& item) {
0 commit comments