/******************************************************************************/
/* */
/* X r d O s s M i o . c c */
/* */
/* (c) 2005 by the Board of Trustees of the Leland Stanford, Jr., University */
/* All Rights Reserved */
/* Produced by Andrew Hanushevsky for Stanford University under contract */
/* DE-AC02-76-SFO0515 with the Department of Energy */
/* */
/* This file is part of the XRootD software suite. */
/* */
/* XRootD is free software: you can redistribute it and/or modify it under */
/* the terms of the GNU Lesser General Public License as published by the */
/* Free Software Foundation, either version 3 of the License, or (at your */
/* option) any later version. */
/* */
/* XRootD is distributed in the hope that it will be useful, but WITHOUT */
/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
/* License for more details. */
/* */
/* You should have received a copy of the GNU Lesser General Public License */
/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
/* COPYING (GPL license). If not, see . */
/* */
/* The copyright holder's institutional names and contributor's names may not */
/* be used to endorse or promote products derived from this software without */
/* specific prior written permission of the institution or contributor. */
/******************************************************************************/
#include
#include
#include
#include
#include
#if defined(_POSIX_MAPPED_FILES)
#include
#endif
#include "XrdSys/XrdSysPthread.hh"
#include "XrdOss/XrdOssMio.hh"
#include "XrdOss/XrdOssMioFile.hh"
#include "XrdOss/XrdOssTrace.hh"
/******************************************************************************/
/* S t a t i c V a r i a b l e s */
/******************************************************************************/
XrdOucHash XrdOssMio::MM_Hash;
XrdSysMutex XrdOssMio::MM_Mutex;
XrdOssMioFile *XrdOssMio::MM_Perm = 0;
XrdOssMioFile *XrdOssMio::MM_Idle = 0;
XrdOssMioFile *XrdOssMio::MM_IdleLast = 0;
char XrdOssMio::MM_on = 1;
char XrdOssMio::MM_chk = 0;
char XrdOssMio::MM_okmlock = 1;
char XrdOssMio::MM_preld = 0;
long long XrdOssMio::MM_pagsz = (long long)sysconf(_SC_PAGESIZE);
#ifdef __APPLE__
long long XrdOssMio::MM_pages = 1024*1024*1024;
#else
long long XrdOssMio::MM_pages = (long long)sysconf(_SC_PHYS_PAGES);
#endif
long long XrdOssMio::MM_max = MM_pagsz*MM_pages/2;
long long XrdOssMio::MM_inuse = 0;
extern XrdSysError OssEroute;
extern XrdOucTrace OssTrace;
/******************************************************************************/
/* D i s p l a y */
/******************************************************************************/
void XrdOssMio::Display(XrdSysError &Eroute)
{
char buff[1080];
snprintf(buff, sizeof(buff), " oss.memfile %s%s%s max %lld",
(MM_on ? "" : "off "),
(MM_preld ? "preload" : ""),
(MM_chk ? "check xattr" : ""), MM_max);
Eroute.Say(buff);
}
/******************************************************************************/
/* M a p */
/******************************************************************************/
XrdOssMioFile *XrdOssMio::Map(char *path, int fd, int opts)
{
#if defined(_POSIX_MAPPED_FILES)
EPNAME("MioMap");
XrdSysMutexHelper mapMutex;
struct stat statb;
XrdOssMioFile *mp;
void *thefile;
char hashname[64];
// Get the size of the file
//
if (fstat(fd, &statb))
{OssEroute.Emsg("Mio", errno, "fstat file", path);
return 0;
}
// Develop hash name for this file
//
XrdOucTrace::bin2hex((char *)&statb.st_dev,
int(sizeof(statb.st_dev)), hashname);
XrdOucTrace::bin2hex((char *)&statb.st_ino, int(sizeof(statb.st_ino)),
hashname+(sizeof(statb.st_dev)*2));
// Because of potntial race conditions, we must serialize execution
//
mapMutex.Lock(&MM_Mutex);
// Check if we already have this mapping
//
if ((mp = MM_Hash.Find(hashname)))
{DEBUG("Reusing mmap; usecnt=" <inUse <<" path=" <Status & OSSMIO_MPRM) && !mp->inUse) Reclaim(mp);
mp->inUse++;
return mp;
}
// Check if memory will be over committed
//
if (MM_inuse + statb.st_size > MM_max)
{if (!Reclaim(statb.st_size))
{OssEroute.Emsg("Mio", "Unable to reclaim enough storage to mmap",path);
return 0;
}
}
MM_inuse += statb.st_size;
// Memory map the file
//
if ((thefile = mmap(0,statb.st_size,PROT_READ,MAP_PRIVATE,fd,0))==MAP_FAILED)
{OssEroute.Emsg("Mio", errno, "mmap file", path);
return 0;
} else {DEBUG("mmap " <Base = thefile;
mp->Size = statb.st_size;
mp->Dev = statb.st_dev;
mp->Ino = statb.st_ino;
mp->Status = opts;
// Add the mapping to our hash table
//
if (MM_Hash.Add(hashname, mp))
{OssEroute.Emsg("Mio", "Hash add failed for", path);
munmap((char *)thefile, statb.st_size);
delete mp;
return 0;
}
// If this is a permanent file, place it on the permanent queue
//
if (opts & OSSMIO_MPRM)
{mp->Next = MM_Perm; MM_Perm = mp;
DEBUG("Placed file on permanent queue " <inUse == 1)
{pthread_t tid;
int retc;
mp->inUse++;
if ((retc = XrdSysThread::Run(&tid, preLoad, (void *)mp)) < 0)
{OssEroute.Emsg("Mio", retc, "creating mmap preload thread");
mp->inUse--;
}
else DEBUG("started mmap preload thread; tid=" <<(unsigned long)tid);
}
// All done
//
return mp;
#else
return 0;
#endif
}
/******************************************************************************/
/* p r e L o a d */
/******************************************************************************/
void *XrdOssMio::preLoad(void *arg)
{
XrdOssMioFile *mp = (XrdOssMioFile *)arg;
char *Base = (char *)(mp->Base);
char *Bend = Base + mp->Size;
long long MY_pagsz = MM_pagsz;
// Reference each page until we are done. This is somewhat obtuse but we
// are trying to keep the compiler from optimizing out the code.
//
while(Base < Bend) Base += (*Base ? MY_pagsz : MM_pagsz);
// All done
//
Recycle(mp);
return (void *)0;
}
/******************************************************************************/
/* R e c l a i m */
/******************************************************************************/
// Reclaim() can only be called if the caller has the MM_Mutex lock!
//
int XrdOssMio::Reclaim(off_t amount)
{
EPNAME("MioReclaim");
XrdOssMioFile *mp;
DEBUG("Trying to reclaim " < 0)
{MM_Idle = mp->Next;
MM_inuse -= mp->Size;
amount -= mp->Size;
MM_Hash.Del(mp->HashName); // This will delete the object
}
// Indicate whether we cleared enough
//
return amount <= 0;
}
/******************************************************************************/
int XrdOssMio::Reclaim(XrdOssMioFile *mp)
{
EPNAME("MioReclaim");
XrdOssMioFile *pmp = 0, *cmp = MM_Idle;
// Try to find the mapping
//
while(cmp && mp != cmp) {pmp = cmp; cmp = cmp->Next;}
// Remove mapping from the idle list
//
if (cmp)
{if (pmp) pmp->Next = mp->Next;
else MM_Idle = mp->Next;
if (MM_IdleLast == cmp) MM_IdleLast = pmp;
}
else {DEBUG("Cannot find mapping for " <Dev <<':' <Ino);}
return (cmp != 0);
}
/******************************************************************************/
/* R e c y c l e */
/******************************************************************************/
void XrdOssMio::Recycle(XrdOssMioFile *mp)
{
XrdSysMutexHelper mmMutex(&MM_Mutex);
// Decrement the use count
//
mp->inUse--;
if (mp->inUse < 0)
{OssEroute.Emsg("Mio", "MM usecount underflow for ", mp->HashName);
mp->inUse = 0;
} else if (mp->inUse > 0) return;
// If this is not a kept mapping, put it on the reclaim list
//
if (!(mp->Status & OSSMIO_MPRM))
{if (MM_IdleLast) MM_IdleLast->Next = mp;
else MM_Idle = mp;
MM_IdleLast = mp;
mp->Next = 0;
}
}
/******************************************************************************/
/* S e t */
/******************************************************************************/
void XrdOssMio::Set(int V_on, int V_preld, int V_check)
{
if (V_on >= 0) MM_on = (char)V_on;
if (V_preld >= 0) MM_preld = (char)V_preld;
if (V_check >= 0) MM_chk = (char)V_check;
}
void XrdOssMio::Set(long long V_max)
{
if (V_max > 0) MM_max = V_max;
else if (V_max < 0) MM_max = MM_pagsz*MM_pages*(-V_max)/100;
}
/******************************************************************************/
/* X r d O s s d M i o F i l e D e s t r u c t o r */
/******************************************************************************/
XrdOssMioFile::~XrdOssMioFile()
{
#if defined(_POSIX_MAPPED_FILES)
munmap((char *)Base, Size);
#endif
}