00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "Basics.H"
00029 #include "FdCache.H"
00030 #include "ShortIdKey.H"
00031 #include "logging.H"
00032
00033 #include <Thread.H>
00034 #include <fcntl.h>
00035
00036 #include "timing.H"
00037
00038 namespace FdCache
00039 {
00040
00041 struct FdInfo {
00042 int fd;
00043 FdCache::OFlag ofl;
00044 time_t lastUse;
00045
00046 FdInfo* prev;
00047 FdInfo* next;
00048
00049 FdInfo* moreprev;
00050 FdInfo* more;
00051
00052 ShortIdKey key;
00053 };
00054 typedef ::Table<ShortIdKey, FdInfo*>::Default FdTable;
00055 typedef ::Table<ShortIdKey, FdInfo*>::Iterator FdIter;
00056
00057
00058 class PartialCache
00059 {
00060 private:
00061 Basics::mutex mu;
00062 FdTable table;
00063 FdInfo lruListHead;
00064 unsigned int nCached;
00065
00066 Basics::uint64 hit_count, open_miss_count, try_miss_count,
00067 evict_count, expire_count;
00068
00069 void fdiDeleteNL(FdInfo* fdi, char* where);
00070
00071
00072 int lookup(ShortIdKey &key, FdCache::OFlag ofl,
00073 FdCache::OFlag &oflout)
00074 throw ();
00075
00076 public:
00077 PartialCache()
00078 : nCached(0),
00079 hit_count(0), open_miss_count(0), try_miss_count(0),
00080 evict_count(0), expire_count(0)
00081 {
00082 this->lruListHead.prev = this->lruListHead.next = &(this->lruListHead);
00083 }
00084
00085 int open(ShortId sid, FdCache::OFlag ofl, FdCache::OFlag* oflout) throw ();
00086 int tryopen(ShortId sid, FdCache::OFlag ofl, FdCache::OFlag* oflOut =NULL)
00087 throw();
00088 void close(ShortId sid, int fd, OFlag ofl) throw();
00089 void flush(ShortId sid, OFlag ofl) throw();
00090
00091 void janitor_sweep(time_t now) throw();
00092
00093 void accumulateStats( Basics::uint32 &n_in_cache,
00094 Basics::uint64 &hits,
00095 Basics::uint64 &open_misses,
00096 Basics::uint64 &try_misses,
00097 Basics::uint64 &evictions,
00098 Basics::uint64 &expirations);
00099 };
00100 }
00101
00102
00103 static const int JANITOR_SLEEP = 5*60;
00104 static const int JANITOR_TOO_OLD = 2*JANITOR_SLEEP;
00105
00106
00107 static pthread_once_t once = PTHREAD_ONCE_INIT;
00108 static unsigned int maxCached;
00109 static Basics::thread janitor;
00110
00111 static unsigned int fdCache_split_bits = 2, fdCache_count;
00112 static Bit32 fdCache_split_mask;
00113 static FdCache::PartialCache *fdCaches;
00114
00115
00116 void FdCache::PartialCache::fdiDeleteNL(FdCache::FdInfo* fdi, char* where)
00117 {
00118 FdCache::FdInfo* deleted;
00119 if (fdi->moreprev == NULL) {
00120 bool ok = this->table.Delete(fdi->key, deleted, false);
00121 assert(ok);
00122 assert(deleted == fdi);
00123 if (fdi->more != NULL) {
00124 this->table.Put(fdi->key, fdi->more);
00125 }
00126 }
00127 fdi->prev->next = fdi->next;
00128 fdi->next->prev = fdi->prev;
00129 if (fdi->moreprev) fdi->moreprev->more = fdi->more;
00130 if (fdi->more) fdi->more->moreprev = fdi->moreprev;
00131
00132 ::close(fdi->fd);
00133 Repos::dprintf(DBG_FDCACHE, "FdCache::%s deleted old sid 0x%08x, fd %d, ofl %d\n",
00134 where, fdi->key.sid, fdi->fd, fdi->ofl);
00135 delete fdi;
00136 }
00137
00138 void FdCache::PartialCache::janitor_sweep(time_t now) throw()
00139 {
00140 this->mu.lock();
00141 while (this->nCached > 0 &&
00142 (now - this->lruListHead.next->lastUse) >= JANITOR_TOO_OLD) {
00143 this->fdiDeleteNL(this->lruListHead.next, "JanitorThread");
00144 this->nCached--;
00145 this->expire_count++;
00146 }
00147 this->mu.unlock();
00148 }
00149
00150 void *
00151 JanitorThread(void *arg)
00152 {
00153 signal(SIGPIPE, SIG_IGN);
00154 signal(SIGQUIT, SIG_DFL);
00155 signal(SIGSEGV, SIG_DFL);
00156 signal(SIGABRT, SIG_DFL);
00157 signal(SIGBUS, SIG_DFL);
00158 signal(SIGILL, SIG_DFL);
00159
00160 for (;;) {
00161 time_t now = time(NULL);
00162
00163
00164 for(unsigned int i = 0; i < fdCache_count; i++)
00165 {
00166 fdCaches[i].janitor_sweep(now);
00167 }
00168 sleep(JANITOR_SLEEP);
00169 }
00170
00171
00172 }
00173
00174 extern "C"
00175 {
00176 static void
00177 FdCache_init()
00178 {
00179 maxCached = getdtablesize() / 2;
00180 assert(maxCached > 0);
00181
00182
00183
00184 Text value;
00185 if (VestaConfig::get("Repository", "fdCache_split_bits", value)) {
00186 fdCache_split_bits = atoi(value.cchars());
00187 }
00188
00189
00190 fdCache_count = (1 << fdCache_split_bits);
00191 fdCaches = NEW_ARRAY(FdCache::PartialCache, fdCache_count);
00192
00193
00194
00195 fdCache_split_mask = 0;
00196 for(unsigned int i = 0; i < fdCache_split_bits; i++)
00197 {
00198 fdCache_split_mask |= (1 << i);
00199 }
00200 assert(fdCache_split_mask < fdCache_count);
00201
00202 Basics::thread_attr janitor_attr;
00203 #if defined (_POSIX_THREAD_PRIORITY_SCHEDULING) && !defined(__linux__)
00204
00205 janitor_attr.set_schedpolicy(SCHED_RR);
00206 janitor_attr.set_inheritsched(PTHREAD_EXPLICIT_SCHED);
00207 janitor_attr.set_sched_priority(sched_get_priority_min(SCHED_RR));
00208 #endif
00209
00210
00211 janitor.fork(JanitorThread, 0, janitor_attr);
00212 }
00213 }
00214
00215 static unsigned int fdCacheIndex(ShortId sid)
00216 {
00217 unsigned int result = sid & fdCache_split_mask;
00218 assert(result < fdCache_count);
00219 return result;
00220 }
00221
00222 int
00223 FdCache::PartialCache::lookup(ShortIdKey &key, FdCache::OFlag ofl,
00224 FdCache::OFlag &oflout)
00225 throw ()
00226 {
00227 FdCache::FdInfo* fdihead = 0;
00228 int fd = -1;
00229 FdCache::OFlag oflused = FdCache::any;
00230
00231 if (this->table.Delete(key, fdihead, false)) {
00232
00233 FdCache::FdInfo* fdi = fdihead;
00234 do {
00235 FdCache::FdInfo* more = fdi->more;
00236 if (ofl == FdCache::any || ofl == fdi->ofl) {
00237 fd = fdi->fd;
00238 assert(fd != -1);
00239 oflout = fdi->ofl;
00240 fdi->prev->next = fdi->next;
00241 fdi->next->prev = fdi->prev;
00242 if (fdi->moreprev != NULL) {
00243 fdi->moreprev->more = fdi->more;
00244 }
00245 if (fdi->more != NULL) {
00246 fdi->more->moreprev = fdi->moreprev;
00247 }
00248 if (fdihead == fdi) {
00249 fdihead = fdi->more;
00250 }
00251 delete fdi;
00252 this->nCached--;
00253 break;
00254 }
00255 fdi = more;
00256 } while (fdi);
00257 if (fdihead != NULL) {
00258 this->table.Put(key, fdihead);
00259 }
00260 }
00261
00262 return fd;
00263 }
00264
00265 int
00266 FdCache::open(ShortId sid, FdCache::OFlag ofl, FdCache::OFlag* oflout) throw ()
00267 {
00268 pthread_once(&once, FdCache_init);
00269 unsigned int index = fdCacheIndex(sid);
00270 assert(fdCaches != NULL);
00271 return fdCaches[index].open(sid, ofl, oflout);
00272 }
00273
00274 int
00275 FdCache::PartialCache::open(ShortId sid, FdCache::OFlag ofl, FdCache::OFlag* oflout) throw ()
00276 {
00277
00278 FdCache::OFlag oflused = FdCache::any;
00279 ShortIdKey key(sid);
00280
00281 RECORD_TIME_POINT;
00282 this->mu.lock();
00283 RECORD_TIME_POINT;
00284
00285 int fd = this->lookup(key, ofl, oflused);
00286
00287
00288 if(fd != -1)
00289 this->hit_count++;
00290 else
00291 this->open_miss_count++;
00292
00293 this->mu.unlock();
00294 RECORD_TIME_POINT;
00295
00296 if (fd == -1)
00297 {
00298
00299 if (ofl == FdCache::any) {
00300 oflused = FdCache::ro;
00301 } else {
00302 oflused = ofl;
00303 }
00304 RECORD_TIME_POINT;
00305 fd = SourceOrDerived::fdopen(sid, oflused == FdCache::ro ?
00306 O_RDONLY : O_RDWR);
00307 RECORD_TIME_POINT;
00308 Repos::dprintf(DBG_FDCACHE, "FdCache::open 0x%08x opened fd %d ofl %d=>%d\n",
00309 sid, fd, ofl, oflused);
00310 }
00311 else
00312 {
00313 Repos::dprintf(DBG_FDCACHE,
00314 "FdCache::open 0x%08x found fd %d ofl %d=>%d\n",
00315 sid, fd, ofl, oflused);
00316 }
00317 assert(oflused != FdCache::any);
00318 if (oflout) *oflout = oflused;
00319 return fd;
00320 }
00321
00322 int FdCache::tryopen(ShortId sid, FdCache::OFlag ofl, FdCache::OFlag* oflout)
00323 throw ()
00324 {
00325 pthread_once(&once, FdCache_init);
00326 unsigned int index = fdCacheIndex(sid);
00327 assert(fdCaches != NULL);
00328 return fdCaches[index].tryopen(sid, ofl, oflout);
00329 }
00330
00331 int
00332 FdCache::PartialCache::tryopen(ShortId sid, FdCache::OFlag ofl, FdCache::OFlag* oflout)
00333 throw ()
00334 {
00335
00336 FdCache::OFlag oflused = FdCache::any;
00337 ShortIdKey key(sid);
00338
00339 RECORD_TIME_POINT;
00340 this->mu.lock();
00341 RECORD_TIME_POINT;
00342
00343 int fd = this->lookup(key, ofl, oflused);
00344
00345
00346 if(fd != -1)
00347 this->hit_count++;
00348 else
00349 this->try_miss_count++;
00350
00351 this->mu.unlock();
00352 RECORD_TIME_POINT;
00353
00354 if(fd != -1)
00355 {
00356 assert(oflused != FdCache::any);
00357 if (oflout) *oflout = oflused;
00358 Repos::dprintf(DBG_FDCACHE,
00359 "FdCache::tryopen 0x%08x found fd %d ofl %d=>%d\n",
00360 sid, fd, ofl, oflused);
00361 }
00362 return fd;
00363 }
00364
00365 void
00366 FdCache::close(ShortId sid, int fd, FdCache::OFlag ofl) throw ()
00367 {
00368 pthread_once(&once, FdCache_init);
00369 unsigned int index = fdCacheIndex(sid);
00370 assert(fdCaches != NULL);
00371 fdCaches[index].close(sid, fd, ofl);
00372 }
00373
00374 void
00375 FdCache::PartialCache::close(ShortId sid, int fd, FdCache::OFlag ofl) throw ()
00376 {
00377 RECORD_TIME_POINT;
00378 this->mu.lock();
00379 RECORD_TIME_POINT;
00380 assert(fd != -1);
00381 ShortIdKey key(sid);
00382 FdCache::FdInfo* fdi;
00383 FdCache::FdInfo* oldfdi;
00384 if (!this->table.Delete(key, oldfdi, false)) {
00385 oldfdi = NULL;
00386 }
00387 fdi = NEW(FdCache::FdInfo);
00388 fdi->fd = fd;
00389 assert(ofl != FdCache::any);
00390 fdi->ofl = ofl;
00391 fdi->lastUse = time(NULL);
00392 fdi->next = &this->lruListHead;
00393 fdi->prev = this->lruListHead.prev;
00394 fdi->prev->next = fdi;
00395 fdi->next->prev = fdi;
00396 fdi->key = key;
00397 fdi->more = oldfdi;
00398 fdi->moreprev = NULL;
00399 if (oldfdi) oldfdi->moreprev = fdi;
00400 this->table.Put(key, fdi);
00401 Repos::dprintf(DBG_FDCACHE, "FdCache::close 0x%08x cached fd %d %s\n",
00402 sid, fd, oldfdi ? "(dupe)" : "");
00403 this->nCached++;
00404 if (this->nCached > (maxCached / fdCache_count)) {
00405 fdiDeleteNL(lruListHead.next, "close");
00406 this->nCached--;
00407 this->evict_count++;
00408 }
00409 this->mu.unlock();
00410 RECORD_TIME_POINT;
00411 }
00412
00413 void
00414 FdCache::flush(ShortId sid, FdCache::OFlag ofl) throw ()
00415 {
00416 pthread_once(&once, FdCache_init);
00417 unsigned int index = fdCacheIndex(sid);
00418 assert(fdCaches != NULL);
00419 fdCaches[index].flush(sid, ofl);
00420 }
00421
00422 void
00423 FdCache::PartialCache::flush(ShortId sid, FdCache::OFlag ofl) throw ()
00424 {
00425 this->mu.lock();
00426 ShortIdKey key(sid);
00427 FdCache::FdInfo* fdihead;
00428 if (this->table.Delete(key, fdihead, false)) {
00429 FdCache::FdInfo* fdi = fdihead;
00430 do {
00431 FdCache::FdInfo* more = fdi->more;
00432 if (ofl == FdCache::any || ofl == fdi->ofl) {
00433 fdi->prev->next = fdi->next;
00434 fdi->next->prev = fdi->prev;
00435 if (fdi->moreprev != NULL) {
00436 fdi->moreprev->more = fdi->more;
00437 }
00438 if (fdi->more != NULL) {
00439 fdi->more->moreprev = fdi->moreprev;
00440 }
00441 if (fdihead == fdi) {
00442 fdihead = fdi->more;
00443 }
00444 Repos::dprintf(DBG_FDCACHE,
00445 "FdCache::flush 0x%08x deleted fd %d ofl %d\n",
00446 sid, fdi->fd, fdi->ofl);
00447 ::close(fdi->fd);
00448 delete fdi;
00449 this->nCached--;
00450 }
00451 fdi = more;
00452 } while (fdi);
00453 if (fdihead != NULL) {
00454 this->table.Put(key, fdihead);
00455 }
00456 }
00457 this->mu.unlock();
00458 }
00459
00460 void
00461 FdCache::getStats( Basics::uint32 &n_in_cache,
00462 Basics::uint64 &hits,
00463 Basics::uint64 &open_misses,
00464 Basics::uint64 &try_misses,
00465 Basics::uint64 &evictions,
00466 Basics::uint64 &expirations)
00467 {
00468
00469
00470 n_in_cache = 0;
00471 hits = 0;
00472 open_misses = 0;
00473 try_misses = 0;
00474 evictions = 0;
00475 expirations = 0;
00476
00477
00478
00479 for(unsigned int i = 0; i < fdCache_count; i++)
00480 {
00481 fdCaches[i].accumulateStats(n_in_cache,
00482 hits, open_misses, try_misses,
00483 evictions, expirations);
00484 }
00485 }
00486
00487 void
00488 FdCache::PartialCache::accumulateStats( Basics::uint32 &n_in_cache,
00489 Basics::uint64 &hits,
00490 Basics::uint64 &open_misses,
00491 Basics::uint64 &try_misses,
00492 Basics::uint64 &evictions,
00493 Basics::uint64 &expirations)
00494 {
00495 this->mu.lock();
00496 n_in_cache += this->nCached;
00497 hits += this->hit_count;
00498 open_misses += this->open_miss_count;
00499 try_misses += this->try_miss_count;
00500 evictions += this->evict_count;
00501 expirations += this->expire_count;
00502 this->mu.unlock();
00503 }