@@ -15,6 +15,7 @@ limitations under the License.
1515*/
1616
1717#include " cache_pool.h"
18+ #include < cassert>
1819#include < dirent.h>
1920#include < fcntl.h>
2021#include < sys/stat.h>
@@ -117,6 +118,7 @@ void FileCachePool::probeFiemap() {
117118}
118119
119120ICacheStore* FileCachePool::do_open (std::string_view pathname, int flags, mode_t mode) {
121+ SCOPED_LOCK (m_lock_);
120122 auto localFile = openMedia (pathname, flags, mode);
121123 if (!localFile) {
122124 return nullptr ;
@@ -169,43 +171,56 @@ int FileCachePool::stat(CacheStat* stat, std::string_view pathname) {
169171 return -1 ;
170172}
171173
172- int FileCachePool::evict (std::string_view filename) {
173- // Check cold tiers first
174- for (auto * tier : coldTiers_) {
175- if (tier->contains (filename)) {
176- auto freed = truncateAndUnlink (filename);
177- tier->remove (filename);
178- return freed >= 0 ? 0 : -1 ;
179- }
180- }
181-
182- auto fileIter = fileIndex_.find (filename);
183- if (fileIter == fileIndex_.end ()) {
184- LOG_ERROR (" Evict no such file , name: `" , filename);
185- return 0 ;
186- }
187-
188- const auto & filePath = fileIter->first ;
189- auto lruEntry = fileIter->second .get ();
190- if (lruEntry->openCount == 0 ) {
191- lru_.mark_key_cleared (lruEntry->lruIter );
192- }
174+ // Opens `name`, truncates it, then finalizes eviction.
175+ // MUST be called WITHOUT m_lock_ held.
176+ bool FileCachePool::evictOpenedFile (const std::string& name) {
193177 int err = 0 ;
194- auto cacheStore = static_cast <FileCacheStore*>(open (filePath , O_RDWR , 0644 ));
178+ auto cacheStore = static_cast <FileCacheStore*>(open (name , O_RDWR , 0644 ));
195179 if (cacheStore) {
196180 DEFER (cacheStore->release ());
197181 photon::scoped_rwlock rl (cacheStore->rw_lock (), photon::WLOCK );
198182 err = cacheStore->evict (0 );
199- lruEntry->truncate_done = false ;
200183 }
201184 if (err) {
202185 ERRNO e;
203- LOG_ERROR (" truncate(0) failed, name: `, ret: `, error code: `" , filePath,
204- err, e);
205- // If truncate fails, we can attempt to remove the file directly
206- // in the afterFtrucate function.
186+ LOG_ERROR (" truncate(0) failed, name: `, ret: `, error code: `" , name, err, e);
187+ // Fall through: afterFtrucate can still remove the file directly.
188+ }
189+ return finalizeEvicted (name);
190+ }
191+
192+ // Acquires m_lock_ and finalizes eviction bookkeeping for `name`.
193+ bool FileCachePool::finalizeEvicted (const std::string& name) {
194+ SCOPED_LOCK (m_lock_);
195+ auto it = fileIndex_.find (name);
196+ if (it == fileIndex_.end ()) return true ; // already evicted concurrently
197+ it->second ->truncate_done = false ;
198+ return afterFtrucate (it);
199+ }
200+
201+ int FileCachePool::evict (std::string_view filename) {
202+ std::string name (filename);
203+ {
204+ SCOPED_LOCK (m_lock_);
205+ // Check cold tiers first
206+ for (auto * tier : coldTiers_) {
207+ if (tier->contains (filename)) {
208+ auto freed = evictColdVictim (tier, filename);
209+ return freed >= 0 ? 0 : -1 ;
210+ }
211+ }
212+
213+ auto fileIter = fileIndex_.find (name);
214+ if (fileIter == fileIndex_.end ()) {
215+ LOG_ERROR (" Evict no such file , name: `" , filename);
216+ return 0 ;
217+ }
218+ auto lruEntry = fileIter->second .get ();
219+ if (lruEntry->openCount == 0 ) {
220+ lru_.mark_key_cleared (lruEntry->lruIter );
221+ }
207222 }
208- return afterFtrucate (fileIter ) ? 0 : -1 ;
223+ return evictOpenedFile (name ) ? 0 : -1 ;
209224}
210225
211226int FileCachePool::evict (size_t size) {
@@ -223,6 +238,7 @@ bool FileCachePool::isFull() {
223238}
224239
225240void FileCachePool::removeOpenFile (FileNameMap::iterator iter) {
241+ SCOPED_LOCK (m_lock_);
226242 iter->second ->openCount --;
227243}
228244
@@ -231,11 +247,18 @@ void FileCachePool::forceRecycle() {
231247}
232248
233249void FileCachePool::updateLru (FileNameMap::iterator iter) {
250+ SCOPED_LOCK (m_lock_);
234251 lru_.access (iter->second ->lruIter );
235252}
236253
237254// currently, we exist duplicate pwrite
238- int64_t FileCachePool::updateSpace (FileNameMap::iterator iter, uint64_t size) {
255+ int64_t FileCachePool::updateSpace (FileNameMap::iterator iter, IFile* localFile) {
256+ SCOPED_LOCK (m_lock_);
257+ struct stat st = {};
258+ if (localFile->fstat (&st) != 0 ) {
259+ LOG_ERRNO_RETURN (0 , 0 , " fstat failed" );
260+ }
261+ uint64_t size = kDiskBlockSize * st.st_blocks ;
239262 auto lruEntry = iter->second .get ();
240263 auto diff = static_cast <int64_t >(size) - static_cast <int64_t >(lruEntry->size );
241264 totalUsed_ += diff;
@@ -257,13 +280,7 @@ int64_t FileCachePool::updateSpace(FileNameMap::iterator iter, uint64_t size) {
257280 LOG_WARN (" disk free space below floor `, force recycle" , diskAvailInBytes_);
258281 }
259282 }
260-
261- if (full) {
262- isFull_ = true ;
263- forceRecycle ();
264- if (lruEntry->size ==0 ) diff = 0 ;// in some extream condition ,
265- // forceRecycle maybe truncate current file to 0
266- }
283+ if (full) isFull_ = true ;
267284 return diff;
268285}
269286
@@ -279,10 +296,10 @@ bool FileCachePool::diskSpaceLow() {
279296
280297uint64_t FileCachePool::timerHandler (void * data) {
281298 auto cur = static_cast <FileCachePool*>(data);
282- if (cur->running_ ) {
299+ // Atomic test-and-set: only one vCPU runs eviction at a time.
300+ if (cur->running_ .exchange (true )) {
283301 return 0 ;
284302 }
285- cur->running_ = true ;
286303 DEFER (cur->running_ = false ;);
287304 cur->eviction ();
288305 return 0 ;
@@ -309,79 +326,71 @@ void FileCachePool::eviction() {
309326 }
310327 }
311328
312- if (totalUsed_ >= static_cast <int64_t >(waterMark_)) {
313- evictByCache = totalUsed_ - waterMark_;
314- }
329+ int64_t actualEvict;
330+ {
331+ SCOPED_LOCK (m_lock_);
332+ if (totalUsed_ >= static_cast <int64_t >(waterMark_)) {
333+ evictByCache = totalUsed_ - waterMark_;
334+ }
315335
316- auto actualEvict = std::min (
317- static_cast <int64_t >(std::max (evictByCache, evictByDisk)),
318- totalUsed_
319- );
336+ actualEvict = std::min (
337+ static_cast <int64_t >(std::max (evictByCache, evictByDisk)),
338+ totalUsed_
339+ );
320340
321- if (actualEvict <= 0 ) {
322- return ;
323- }
341+ if (actualEvict <= 0 ) {
342+ return ;
343+ }
324344
325- isFull_ = true ;
345+ isFull_ = true ;
326346
327- // Evict from cold tiers first in reverse order
328- for (int i = coldTiers_.size () - 1 ; i >= 0 ; i--) {
329- auto * tier = coldTiers_[i];
330- while (actualEvict > 0 && !tier->empty () && !exit_) {
331- auto name = tier->victim ();
332- auto freed = truncateAndUnlink (name);
333- tier->remove (name);
334- if (freed >= 0 ) actualEvict -= freed;
335- photon::thread_yield ();
347+ for (auto i = coldTiers_.size (); i > 0 && actualEvict > 0 && !exit_; --i) {
348+ auto * tier = coldTiers_[i - 1 ];
349+ while (actualEvict > 0 && !tier->empty () && !exit_) {
350+ auto freed = evictColdVictim (tier, tier->victim ());
351+ if (freed >= 0 ) actualEvict -= freed;
352+ }
336353 }
337- }
338354
339- if (!lru_.empty () && !exit_) {
340- LOG_AUDIT (" eviction" , VALUE (actualEvict), VALUE (evictByCache), VALUE (evictByDisk), VALUE (totalUsed_));
355+ if (!lru_.empty () && !exit_) {
356+ LOG_AUDIT (" eviction" , VALUE (actualEvict), VALUE (evictByCache), VALUE (evictByDisk), VALUE (totalUsed_));
357+ }
341358 }
342359
343360 uint64_t empty_files_sequence = 0 ;
344- while (actualEvict > 0 && !lru_.empty () && !exit_) {
345- auto fileIter = lru_.back ();
346- const auto & fileName = fileIter->first ;
347- auto lruEntry = fileIter->second .get ();
348- auto fileSize = lruEntry->size ;
349- if (lruEntry->openCount == 0 ){
350- lru_.mark_key_cleared (fileIter->second ->lruIter );
351- } else {
352- lru_.access (fileIter->second ->lruIter );
353- }
354- // as soon as possible truncate and unlink
355- if (0 == fileSize) {
356- if (0 == fileIter->second ->openCount ) {
357- afterFtrucate (fileIter);
361+ while (actualEvict > 0 && !exit_) {
362+ std::string fileName;
363+ uint64_t fileSize;
364+ {
365+ SCOPED_LOCK (m_lock_);
366+ if (lru_.empty () || totalUsed_ <= 0 ) break ;
367+ auto fileIter = lru_.back ();
368+ fileName = std::string (fileIter->first );
369+ auto lruEntry = fileIter->second .get ();
370+ fileSize = lruEntry->size ;
371+ if (lruEntry->openCount == 0 ) {
372+ lru_.mark_key_cleared (lruEntry->lruIter );
373+ } else {
374+ lru_.access (lruEntry->lruIter );
375+ }
376+ if (0 == fileSize) {
377+ if (0 == lruEntry->openCount ) {
378+ afterFtrucate (fileIter);
358379 } else {
359380 empty_files_sequence++;
360- if (empty_files_sequence = = lru_.size ()) {
381+ if (empty_files_sequence > = lru_.size ()) {
361382 LOG_ERROR (" eviction: all ` LRU entries have size=0 with openCount>0, "
362383 " cannot make progress. actualEvict=`, totalUsed=`" ,
363384 lru_.size (), actualEvict, totalUsed_);
364385 break ;
365386 }
366387 }
367- continue ;
368- }
369-
370- empty_files_sequence = 0 ;
371- int err = 0 ;
372- auto cacheStore = static_cast <FileCacheStore*>(open (fileName, O_RDWR , 0644 ));
373- if (cacheStore) {
374- DEFER (cacheStore->release ());
375- photon::scoped_rwlock rl (cacheStore->rw_lock (), photon::WLOCK );
376- err = cacheStore->evict (0 );
377- lruEntry->truncate_done = false ;
378- }
379-
380- if (err) {
381- ERRNO e;
382- LOG_ERROR (" truncate(0) failed, name : `, ret : `, error code : `" , fileName, err, e);
388+ continue ; // releases m_lock_ via SCOPED_LOCK scope
389+ }
390+ empty_files_sequence = 0 ;
383391 }
384- afterFtrucate (fileIter);
392+ // open()+WLOCK+finalize with m_lock_ released.
393+ evictOpenedFile (fileName);
385394 actualEvict -= fileSize;
386395 photon::thread_yield ();
387396 }
@@ -392,6 +401,7 @@ uint64_t FileCachePool::calcWaterMark(uint64_t capacity, uint64_t maxFreeSpace)
392401 capacity > maxFreeSpace ? capacity - maxFreeSpace : 0 );
393402}
394403
404+ // With m_lock_ held.
395405bool FileCachePool::afterFtrucate (FileNameMap::iterator iter) {
396406 auto lruEntry = iter->second .get ();
397407 totalUsed_ -= static_cast <int64_t >(lruEntry->size );
@@ -432,6 +442,7 @@ int FileCachePool::insertFile(std::string_view file) {
432442 }
433443 auto fileSize = st.st_blocks * kDiskBlockSize ;
434444
445+ SCOPED_LOCK (m_lock_);
435446 auto lruIter = lru_.push_front (fileIndex_.end ());
436447 auto entry = std::unique_ptr<LruEntry>(new LruEntry{lruIter, 0 , fileSize});
437448 auto iter = fileIndex_.emplace (file, std::move (entry)).first ;
@@ -459,6 +470,7 @@ void FileCachePool::adaptThresholds() {
459470 tierHits_.fill (0 );
460471}
461472
473+ // With m_lock_ held.
462474void FileCachePool::demoteToCold () {
463475 while (lru_.size () > thresholds_[0 ].value ) {
464476 auto tailIt = lru_.back ();
@@ -478,6 +490,7 @@ void FileCachePool::demoteToCold() {
478490 }
479491}
480492
493+ // With m_lock_ held.
481494void FileCachePool::promoteToHot (std::string_view filename) {
482495 auto find = fileIndex_.find (filename);
483496 if (find != fileIndex_.end ()) {
@@ -511,6 +524,18 @@ void FileCachePool::promoteToHot(std::string_view filename) {
511524 lru_.front () = iter;
512525}
513526
527+ // With m_lock_ held.
528+ ssize_t FileCachePool::evictColdVictim (ColdCacheTier* tier, std::string_view name) {
529+ auto freed = truncateAndUnlink (name);
530+ tier->remove (name);
531+ if (freed >= 0 ) {
532+ totalUsed_ -= freed;
533+ if (totalUsed_ < 0 ) totalUsed_ = 0 ;
534+ }
535+ return freed;
536+ }
537+
538+ // I/O only: does NOT touch totalUsed_ and does NOT require m_lock_.
514539ssize_t FileCachePool::truncateAndUnlink (std::string_view filename) {
515540 struct stat st = {};
516541 uint64_t fileSize = 0 ;
@@ -523,8 +548,6 @@ ssize_t FileCachePool::truncateAndUnlink(std::string_view filename) {
523548 if (err) {
524549 LOG_ERRNO_RETURN (0 , -1 , " truncate(0) failed, name : `" , filename);
525550 }
526- totalUsed_ -= static_cast <int64_t >(fileSize);
527- if (totalUsed_ < 0 ) totalUsed_ = 0 ;
528551 }
529552 int err = mediaFs_->unlink (filename.data ());
530553 if (err) {
0 commit comments