00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 const char *XrdOssCacheCVSID = "$Id: XrdOssCache.cc 34000 2010-06-21 06:49:56Z ganis $";
00014
00015 #include <unistd.h>
00016 #include <errno.h>
00017 #include <fcntl.h>
00018 #include <stdio.h>
00019 #include <strings.h>
00020 #include <time.h>
00021 #include <sys/param.h>
00022 #include <sys/types.h>
00023 #include <sys/stat.h>
00024
00025 #include "XrdOss/XrdOssCache.hh"
00026 #include "XrdOss/XrdOssOpaque.hh"
00027 #include "XrdOss/XrdOssPath.hh"
00028 #include "XrdOss/XrdOssSpace.hh"
00029 #include "XrdOss/XrdOssTrace.hh"
00030 #include "XrdSys/XrdSysHeaders.hh"
00031 #include "XrdSys/XrdSysPlatform.hh"
00032
00033
00034
00035
00036
00037 extern XrdSysError OssEroute;
00038
00039 extern XrdOucTrace OssTrace;
00040
00041 XrdOssCache_Group *XrdOssCache_Group::fsgroups = 0;
00042
00043 long long XrdOssCache_Group::PubQuota = -1;
00044
00045 XrdSysMutex XrdOssCache::Mutex;
00046 long long XrdOssCache::fsTotal = 0;
00047 long long XrdOssCache::fsLarge = 0;
00048 long long XrdOssCache::fsTotFr = 0;
00049 long long XrdOssCache::fsFree = 0;
00050 long long XrdOssCache::fsSize = 0;
00051 XrdOssCache_FS *XrdOssCache::fsfirst = 0;
00052 XrdOssCache_FS *XrdOssCache::fslast = 0;
00053 XrdOssCache_FSData *XrdOssCache::fsdata = 0;
00054 double XrdOssCache::fuzAlloc= 0.0;
00055 long long XrdOssCache::minAlloc= 0;
00056 int XrdOssCache::fsCount = 0;
00057 int XrdOssCache::ovhAlloc= 0;
00058 int XrdOssCache::Quotas = 0;
00059 int XrdOssCache::Usage = 0;
00060
00061
00062
00063
00064
00065 XrdOssCache_FSData::XrdOssCache_FSData(const char *fsp,
00066 STATFS_t &fsbuff,
00067 dev_t fsID)
00068 {
00069
00070 path = strdup(fsp);
00071 size = static_cast<long long>(fsbuff.f_blocks)
00072 * static_cast<long long>(fsbuff.FS_BLKSZ);
00073 frsz = static_cast<long long>(fsbuff.f_bavail)
00074 * static_cast<long long>(fsbuff.FS_BLKSZ);
00075 XrdOssCache::fsTotal += size;
00076 XrdOssCache::fsTotFr += frsz;
00077 XrdOssCache::fsCount++;
00078 if (size > XrdOssCache::fsLarge) XrdOssCache::fsLarge= size;
00079 if (frsz > XrdOssCache::fsFree) XrdOssCache::fsFree = frsz;
00080 fsid = fsID;
00081 updt = time(0);
00082 next = 0;
00083 stat = 0;
00084 }
00085
00086
00087
00088
00089
00090
00091
00092 XrdOssCache_FS::XrdOssCache_FS(int &retc,
00093 const char *fsGrp,
00094 const char *fsPath,
00095 FSOpts fsOpts)
00096 {
00097 static const mode_t theMode = S_IRWXU | S_IRWXG;
00098 STATFS_t fsbuff;
00099 struct stat sfbuff;
00100 XrdOssCache_FSData *fdp;
00101 XrdOssCache_FS *fsp;
00102
00103
00104
00105 path = group = 0;
00106
00107
00108
00109 fsp = XrdOssCache::fsfirst;
00110 while(fsp && (strcmp(fsp->path,fsPath)||strcmp(fsp->fsgroup->group,fsGrp)))
00111 if ((fsp = fsp->next) == XrdOssCache::fsfirst) {fsp = 0; break;}
00112 if (fsp) {retc = EEXIST; return;}
00113
00114
00115
00116 if (!(fsOpts & isXA)) path = strdup(fsPath);
00117 else {path = XrdOssPath::genPath(fsPath, fsGrp, suffix);
00118 if (mkdir(path, theMode) && errno != EEXIST) {retc=errno; return;}
00119 }
00120 plen = strlen(path);
00121 group = strdup(fsGrp);
00122 fsgroup= 0;
00123 opts = fsOpts;
00124 retc = ENOMEM;
00125
00126
00127
00128 if (FS_Stat(fsPath, &fsbuff) || stat(fsPath, &sfbuff)) {retc=errno; return;}
00129
00130
00131
00132 fdp = XrdOssCache::fsdata;
00133 while(fdp) {if (fdp->fsid == sfbuff.st_dev) break; fdp = fdp->next;}
00134
00135
00136
00137 if (!fdp)
00138 {if (!(fdp = new XrdOssCache_FSData(fsPath,fsbuff,sfbuff.st_dev))) return;
00139 else {fdp->next = XrdOssCache::fsdata; XrdOssCache::fsdata = fdp;}
00140 }
00141
00142
00143
00144 fsdata = fdp;
00145 retc = 0;
00146
00147
00148
00149 if (!XrdOssCache::fsfirst) {next = this;
00150 XrdOssCache::fsfirst = this;
00151 XrdOssCache::fslast = this;
00152 }
00153 else {next = XrdOssCache::fslast->next;
00154 XrdOssCache::fslast->next = this;
00155 XrdOssCache::fslast = this;
00156 }
00157
00158
00159
00160 fsgroup = XrdOssCache_Group::fsgroups;
00161 while(fsgroup && strcmp(group, fsgroup->group)) fsgroup = fsgroup->next;
00162 if (!fsgroup && (fsgroup = new XrdOssCache_Group(group, this)))
00163 {fsgroup->next = XrdOssCache_Group::fsgroups;
00164 XrdOssCache_Group::fsgroups=fsgroup;
00165 }
00166 }
00167
00168
00169
00170
00171
00172
00173
00174
00175 int XrdOssCache_FS::Add(const char *fsPath)
00176 {
00177 STATFS_t fsbuff;
00178 struct stat sfbuff;
00179 XrdOssCache_FSData *fdp;
00180
00181
00182
00183 if (FS_Stat(fsPath, &fsbuff) || stat(fsPath, &sfbuff)) return -errno;
00184
00185
00186
00187 fdp = XrdOssCache::fsdata;
00188 while(fdp) {if (fdp->fsid == sfbuff.st_dev) break; fdp = fdp->next;}
00189 if (fdp) return 0;
00190
00191
00192
00193 if (!(fdp = new XrdOssCache_FSData(fsPath,fsbuff,sfbuff.st_dev)))
00194 return -ENOMEM;
00195 fdp->next = XrdOssCache::fsdata;
00196 XrdOssCache::fsdata = fdp;
00197 return 0;
00198 }
00199
00200
00201
00202
00203
00204 long long XrdOssCache_FS::freeSpace(long long &Size, const char *path)
00205 {
00206 STATFS_t fsbuff;
00207 long long fSpace;
00208
00209
00210
00211 if (path)
00212 {if (FS_Stat(path, &fsbuff)) return -1;
00213 Size = static_cast<long long>(fsbuff.f_blocks)
00214 * static_cast<long long>(fsbuff.FS_BLKSZ);
00215 return static_cast<long long>(fsbuff.f_bavail)
00216 * static_cast<long long>(fsbuff.FS_BLKSZ);
00217 }
00218
00219
00220
00221 XrdOssCache::Mutex.Lock();
00222 fSpace = XrdOssCache::fsFree;
00223 Size = XrdOssCache::fsSize;
00224 XrdOssCache::Mutex.UnLock();
00225 return fSpace;
00226 }
00227
00228
00229
00230 long long XrdOssCache_FS::freeSpace(XrdOssCache_Space &Space, const char *path)
00231 {
00232 STATFS_t fsbuff;
00233
00234
00235
00236 if (!path || FS_Stat(path, &fsbuff)) return -1;
00237
00238 Space.Total = static_cast<long long>(fsbuff.f_blocks)
00239 * static_cast<long long>(fsbuff.FS_BLKSZ);
00240 Space.Free = static_cast<long long>(fsbuff.f_bavail)
00241 * static_cast<long long>(fsbuff.FS_BLKSZ);
00242 Space.Inodes= static_cast<long long>(fsbuff.f_files);
00243 Space.Inleft= static_cast<long long>(fsbuff.FS_FFREE);
00244
00245 return Space.Free;
00246 }
00247
00248
00249
00250
00251
00252 int XrdOssCache_FS::getSpace(XrdOssCache_Space &Space, const char *sname)
00253 {
00254 XrdOssCache_Group *fsg = XrdOssCache_Group::fsgroups;
00255
00256
00257
00258 while(fsg && strcmp(sname, fsg->group)) fsg = fsg->next;
00259 if (!fsg) return 0;
00260
00261
00262
00263 return getSpace(Space, fsg);
00264 }
00265
00266
00267
00268 int XrdOssCache_FS::getSpace(XrdOssCache_Space &Space, XrdOssCache_Group *fsg)
00269 {
00270 XrdOssCache_FS *fsp;
00271 XrdOssCache_FSData *fsd;
00272 int pnum = 0;
00273
00274
00275
00276 Space.Total = 0;
00277 Space.Free = 0;
00278
00279
00280
00281 XrdOssCache::Mutex.Lock();
00282 Space.Usage = fsg->Usage; Space.Quota = fsg->Quota;
00283 if ((fsp = XrdOssCache::fsfirst)) do
00284 {if (fsp->fsgroup == fsg)
00285 {fsd = fsp->fsdata; pnum++;
00286 Space.Total += fsd->size; Space.Free += fsd->frsz;
00287 if (fsd->frsz > Space.Maxfree) Space.Maxfree = fsd->frsz;
00288 if (fsd->size > Space.Largest) Space.Largest = fsd->size;
00289 }
00290 fsp = fsp->next;
00291 } while(fsp != XrdOssCache::fsfirst);
00292 XrdOssCache::Mutex.UnLock();
00293
00294
00295
00296 return pnum;
00297 }
00298
00299
00300
00301
00302
00303 void XrdOssCache::Adjust(dev_t devid, off_t size)
00304 {
00305 EPNAME("Adjust")
00306 XrdOssCache_FSData *fsdp;
00307 XrdOssCache_Group *fsgp;
00308
00309
00310
00311 fsdp = XrdOssCache::fsdata;
00312 while(fsdp && fsdp->fsid != devid) fsdp = fsdp->next;
00313 if (!fsdp) {DEBUG("dev " <<devid <<" not found."); return;}
00314
00315
00316
00317 fsgp = XrdOssCache_Group::fsgroups;
00318 while(fsgp && strcmp("public", fsgp->group)) fsgp = fsgp->next;
00319
00320
00321
00322 if (fsdp)
00323 {DEBUG("free=" <<fsdp->frsz <<'-' <<size <<" path=" <<fsdp->path);
00324 Mutex.Lock();
00325 if ( (fsdp->frsz -= size) < 0) fsdp->frsz = 0;
00326 fsdp->stat |= XrdOssFSData_ADJUSTED;
00327 if (fsgp && (fsgp->Usage += size) < 0) fsgp->Usage = 0;
00328 Mutex.UnLock();
00329 } else {
00330 DEBUG("dev " <<devid <<" not found.");
00331 }
00332 }
00333
00334
00335
00336 void XrdOssCache::Adjust(const char *Path, off_t size, struct stat *buf)
00337 {
00338 EPNAME("Adjust")
00339 XrdOssCache_FS *fsp;
00340
00341
00342
00343 if (buf)
00344 {if ((buf->st_mode & S_IFMT) != S_IFLNK) Adjust(buf->st_dev, size);
00345 else {char lnkbuff[MAXPATHLEN+64];
00346 int lnklen = readlink(Path, lnkbuff, sizeof(lnkbuff)-1);
00347 if (lnklen > 0)
00348 {XrdOssPath::Trim2Base(lnkbuff+lnklen-1);
00349 Adjust(lnkbuff, size);
00350 }
00351 }
00352 return;
00353 }
00354
00355
00356
00357 fsp = fsfirst;
00358 while(fsp && strcmp(fsp->path, Path))
00359 if ((fsp = fsp->next) == fsfirst) {fsp = 0; break;}
00360
00361
00362
00363 if (fsp) Adjust(fsp, size);
00364 else {DEBUG("cahe path " <<Path <<" not found.");}
00365 }
00366
00367
00368
00369 void XrdOssCache::Adjust(XrdOssCache_FS *fsp, off_t size)
00370 {
00371 EPNAME("Adjust")
00372 XrdOssCache_FSData *fsdp;
00373
00374
00375
00376 if (fsp)
00377 {fsdp = fsp->fsdata;
00378 DEBUG("used=" <<fsp->fsgroup->Usage <<'+' <<size <<" path=" <<fsp->path);
00379 DEBUG("free=" <<fsdp->frsz <<'-' <<size <<" path=" <<fsdp->path);
00380 Mutex.Lock();
00381 if ((fsp->fsgroup->Usage += size) < 0) fsp->fsgroup->Usage = 0;
00382 if ( (fsdp->frsz -= size) < 0) fsdp->frsz = 0;
00383 fsdp->stat |= XrdOssFSData_ADJUSTED;
00384 if (Usage) XrdOssSpace::Adjust(fsp->fsgroup->GRPid, size);
00385 Mutex.UnLock();
00386 }
00387 }
00388
00389
00390
00391
00392
00393 int XrdOssCache::Alloc(XrdOssCache::allocInfo &aInfo)
00394 {
00395 EPNAME("Alloc");
00396 static const mode_t theMode = S_IRWXU | S_IRWXG;
00397 XrdSysMutexHelper myMutex(&Mutex);
00398 double diffree;
00399 XrdOssPath::fnInfo Info;
00400 XrdOssCache_FS *fsp, *fspend, *fsp_sel;
00401 XrdOssCache_Group *cgp = 0;
00402 long long size, maxfree, curfree;
00403 int rc, madeDir, datfd = 0;
00404
00405
00406
00407 if (!aInfo.cgSize
00408 || (size=aInfo.cgSize*ovhAlloc/100+aInfo.cgSize) < minAlloc)
00409 aInfo.cgSize = size = minAlloc;
00410
00411
00412
00413 cgp = XrdOssCache_Group::fsgroups;
00414 while(cgp && strcmp(aInfo.cgName, cgp->group)) cgp = cgp->next;
00415 if (!cgp) return -ENOENT;
00416 fsp = cgp->curr;
00417
00418
00419
00420 maxfree = fsp->fsdata->frsz;
00421 if (size > maxfree || (aInfo.cgPath && (aInfo.cgPlen > fsp->plen
00422 || strncmp(aInfo.cgPath,fsp->path,aInfo.cgPlen)))) fsp_sel = 0;
00423 else fsp_sel = fsp;
00424 fspend = fsp; fsp = fsp->next;
00425 do {
00426 if (strcmp(aInfo.cgName, fsp->group)
00427 || (aInfo.cgPath && (aInfo.cgPlen > fsp->plen
00428 || strncmp(aInfo.cgPath,fsp->path,aInfo.cgPlen)))) continue;
00429 curfree = fsp->fsdata->frsz;
00430 if (size > curfree) continue;
00431
00432 if (fuzAlloc >= 100) {fsp_sel = fsp; break;}
00433 else if (!fuzAlloc) {if (curfree > maxfree)
00434 {fsp_sel = fsp; maxfree = curfree;}}
00435 else {diffree = (!(curfree + maxfree) ? 0.0
00436 : static_cast<double>(XRDABS(maxfree - curfree)) /
00437 static_cast<double>( maxfree + curfree));
00438 if (diffree > fuzAlloc) {fsp_sel = fsp; maxfree = curfree;}
00439 }
00440 } while((fsp = fsp->next) != fspend);
00441
00442
00443
00444 if (!fsp_sel) return -ENOSPC;
00445 cgp->curr = fsp_sel;
00446
00447
00448
00449 Info.Path = fsp_sel->path;
00450 Info.Plen = fsp_sel->plen;
00451 Info.Sfx = fsp_sel->suffix;
00452 aInfo.cgPsfx = XrdOssPath::genPFN(Info, aInfo.cgPFbf, aInfo.cgPFsz,
00453 (fsp_sel->opts & XrdOssCache_FS::isXA ? 0 : aInfo.Path));
00454
00455
00456
00457 if (!(*aInfo.cgPFbf)) return -ENAMETOOLONG;
00458
00459
00460
00461 if (aInfo.aMode)
00462 {madeDir = 0;
00463 do {do {datfd = open(aInfo.cgPFbf,O_CREAT|O_TRUNC|O_WRONLY,aInfo.aMode);}
00464 while(datfd < 0 && errno == EINTR);
00465 if (datfd >= 0 || errno != ENOENT || madeDir) break;
00466 *Info.Slash='\0'; rc=mkdir(aInfo.cgPFbf,theMode); *Info.Slash='/';
00467 madeDir = 1;
00468 } while(!rc);
00469 if (datfd < 0) return (errno ? -errno : -ENOSYS);
00470 }
00471
00472
00473
00474 DEBUG("free=" <<fsp_sel->fsdata->frsz <<'-' <<size <<" path="
00475 <<fsp_sel->fsdata->path);
00476 fsp_sel->fsdata->frsz -= size;
00477 fsp_sel->fsdata->stat |= XrdOssFSData_REFRESH;
00478 aInfo.cgFSp = fsp_sel;
00479 return datfd;
00480 }
00481
00482
00483
00484
00485
00486 XrdOssCache_FS *XrdOssCache::Find(const char *Path, int lnklen)
00487 {
00488 XrdOssCache_FS *fsp;
00489 char lnkbuff[MAXPATHLEN+64];
00490 struct stat sfbuff;
00491
00492
00493
00494 if (lnklen)
00495 {if (strlcpy(lnkbuff,Path,sizeof(lnkbuff)) >= sizeof(lnkbuff)) return 0;}
00496 else if (lstat(Path, &sfbuff)
00497 || (sfbuff.st_mode & S_IFMT) != S_IFLNK
00498 || (lnklen = readlink(Path,lnkbuff,sizeof(lnkbuff)-1)) <= 0)
00499 return 0;
00500
00501
00502
00503 XrdOssPath::Trim2Base(lnkbuff+lnklen-1);
00504
00505
00506
00507 fsp = fsfirst;
00508 while(fsp && strcmp(fsp->path, lnkbuff))
00509 if ((fsp = fsp->next) == fsfirst) {fsp = 0; break;}
00510 return fsp;
00511 }
00512
00513
00514
00515
00516
00517
00518
00519 int XrdOssCache::Init(const char *UPath, const char *Qfile, int isSOL)
00520 {
00521 XrdOssCache_Group *cgp;
00522 long long bytesUsed;
00523
00524
00525
00526
00527 if ((UPath || Qfile) && !XrdOssSpace::Init(UPath, Qfile, isSOL)) return 1;
00528 if (Qfile) Quotas = !isSOL;
00529 if (UPath) Usage = 1;
00530
00531
00532
00533
00534 if (UPath && (cgp = XrdOssCache_Group::fsgroups))
00535 do {cgp->GRPid = XrdOssSpace::Assign(cgp->group, bytesUsed);
00536 cgp->Usage = bytesUsed;
00537 } while((cgp = cgp->next));
00538 return 0;
00539 }
00540
00541
00542
00543 int XrdOssCache::Init(long long aMin, int ovhd, int aFuzz)
00544 {
00545
00546
00547 minAlloc = aMin;
00548 ovhAlloc = ovhd;
00549 fuzAlloc = static_cast<double>(aFuzz)/100.0;
00550 return 0;
00551 }
00552
00553
00554
00555
00556
00557 void XrdOssCache::List(const char *lname, XrdSysError &Eroute)
00558 {
00559 XrdOssCache_FS *fsp;
00560 const char *theOpt;
00561 char *pP, buff[4096];
00562
00563 if ((fsp = fsfirst)) do
00564 {if (fsp->opts & XrdOssCache_FS::isXA)
00565 {pP = (char *)fsp->path + fsp->plen - 1;
00566 do {pP--;} while(*pP != '/');
00567 *pP = '\0'; theOpt = " xa";
00568 } else {pP=0; theOpt = "";}
00569 snprintf(buff, sizeof(buff), "%s %s %s%s", lname,
00570 fsp->group, fsp->path, theOpt);
00571 if (pP) *pP = '/';
00572 Eroute.Say(buff);
00573 fsp = fsp->next;
00574 } while(fsp != fsfirst);
00575 }
00576
00577
00578
00579
00580
00581 char *XrdOssCache::Parse(const char *token, char *cbuff, int cblen)
00582 {
00583 char *Path;
00584
00585
00586
00587 if (!token || *token == ':')
00588 {strlcpy(cbuff, OSS_CGROUP_DEFAULT, cblen);
00589 return 0;
00590 }
00591
00592
00593
00594 if (!(Path = (char *) index(token, ':'))) strlcpy(cbuff, token, cblen);
00595 else {int n = Path - token;
00596 if (n >= cblen) n = cblen-1;
00597 strncpy(cbuff, token, n); cbuff[n] = '\0';
00598 Path++;
00599 }
00600
00601
00602
00603 return Path;
00604 }
00605
00606
00607
00608
00609
00610 void *XrdOssCache::Scan(int cscanint)
00611 {
00612 EPNAME("CacheScan")
00613 XrdOssCache_FSData *fsdp;
00614 XrdOssCache_Group *fsgp;
00615 const struct timespec naptime = {cscanint, 0};
00616 long long frsz, llT;
00617 int retc, dbgMsg, dbgNoMsg;
00618
00619
00620
00621 if (cscanint > 0 && cscanint < 60) dbgMsg = cscanint/60;
00622 else dbgMsg = 1;
00623 dbgNoMsg = dbgMsg;
00624
00625
00626
00627 while(1)
00628 {if (cscanint > 0) nanosleep(&naptime, 0);
00629
00630
00631
00632 Mutex.Lock();
00633
00634
00635
00636
00637 fsSize = 0;
00638 fsTotFr= 0;
00639 fsFree = 0;
00640 fsdp = fsdata;
00641 while(fsdp)
00642 {retc = 0;
00643 if ((fsdp->stat & XrdOssFSData_REFRESH)
00644 || !(fsdp->stat & XrdOssFSData_ADJUSTED) || cscanint <= 0)
00645 {frsz = XrdOssCache_FS::freeSpace(llT,fsdp->path);
00646 if (frsz < 0) OssEroute.Emsg("CacheScan", errno ,
00647 "state file system ",(char *)fsdp->path);
00648 else {fsdp->frsz = frsz;
00649 fsdp->stat &= ~(XrdOssFSData_REFRESH |
00650 XrdOssFSData_ADJUSTED);
00651 if (!dbgNoMsg--)
00652 {DEBUG("New free=" <<fsdp->frsz <<" path=" <<fsdp->path);
00653 dbgNoMsg = dbgMsg;
00654 }
00655 }
00656 } else fsdp->stat |= XrdOssFSData_REFRESH;
00657 if (!retc)
00658 {if (fsdp->frsz > fsFree)
00659 {fsFree = fsdp->frsz; fsSize = fsdp->size;}
00660 fsTotFr += fsdp->frsz;
00661 }
00662 fsdp = fsdp->next;
00663 }
00664
00665
00666
00667 Mutex.UnLock();
00668 if (cscanint <= 0) return (void *)0;
00669 if (Quotas) XrdOssSpace::Quotas();
00670
00671
00672 if (Usage && XrdOssSpace::Readjust())
00673 {fsgp = XrdOssCache_Group::fsgroups;
00674 Mutex.Lock();
00675 while(fsgp)
00676 {fsgp->Usage = XrdOssSpace::Usage(fsgp->GRPid);
00677 fsgp = fsgp->next;
00678 }
00679 Mutex.UnLock();
00680 }
00681 }
00682
00683
00684
00685 return (void *)0;
00686 }