@@ -1240,6 +1240,19 @@ bool CacheAllocator<CacheTrait>::moveChainedItem(ChainedItem& oldItem,
1240
1240
return true ;
1241
1241
}
1242
1242
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
+
1243
1256
template <typename CacheTrait>
1244
1257
typename CacheAllocator<CacheTrait>::Item*
1245
1258
CacheAllocator<CacheTrait>::findEviction(PoolId pid, ClassId cid) {
@@ -1248,76 +1261,102 @@ CacheAllocator<CacheTrait>::findEviction(PoolId pid, ClassId cid) {
1248
1261
// Keep searching for a candidate until we were able to evict it
1249
1262
// or until the search limit has been exhausted
1250
1263
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 ;
1298
1277
}
1299
1278
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
+ }
1305
1320
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 );
1312
1324
}
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 ();
1313
1344
} 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 ());
1315
1352
}
1316
1353
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;
1321
1360
}
1322
1361
}
1323
1362
return nullptr ;
@@ -2660,126 +2699,6 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
2660
2699
}
2661
2700
}
2662
2701
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
-
2783
2702
template <typename CacheTrait>
2784
2703
typename CacheAllocator<CacheTrait>::WriteHandle
2785
2704
CacheAllocator<CacheTrait>::evictNormalItemForSlabRelease(Item& item) {
0 commit comments