#include <afsconfig.h>
#include <afs/param.h>
-RCSID
- ("$Header$");
#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
+#include <string.h>
#ifdef AFS_NT40_ENV
#include <fcntl.h>
#else
#include <sys/resource.h>
#endif
#endif
-#ifdef HAVE_STRING_H
-#include <string.h>
-#else
-#ifdef HAVE_STRINGS_H
-#include <strings.h>
-#endif
-#endif
+
#include <rx/xdr.h>
#include <afs/afsint.h>
#include <errno.h>
FdHandle_t *fdLruTail;
int ih_Inited = 0;
+int ih_PkgDefaultsSet = 0;
/* Most of the servers use fopen/fdopen. Since the FILE structure
* only has eight bits for the file descriptor, the cache size
* has to be less than 256. The cache can be made larger as long
* as you are sure you don't need fopen/fdopen. */
+
+/* As noted in ihandle.h, the fileno member of FILE on most platforms
+ * in 2008 is a 16- or 32-bit signed int. -Matt
+ */
int fdMaxCacheSize = 0;
int fdCacheSize = 0;
/* Hash table for inode handles */
IHashBucket_t ihashTable[I_HANDLE_HASH_SIZE];
+void *ih_sync_thread(void *);
+
+/* start-time configurable I/O limits */
+ih_init_params vol_io_params;
+
+void ih_PkgDefaults(void)
+{
+ /* once */
+ ih_PkgDefaultsSet = 1;
+
+ /* default to well-known values */
+ vol_io_params.fd_handle_setaside = FD_HANDLE_SETASIDE;
+
+ /* initial fd cachesize. the only one that will be used if
+ * the application does not call ih_UseLargeCache(). set this
+ * to a value representable in fileno member of the system's
+ * FILE structure (or equivalent). */
+ vol_io_params.fd_initial_cachesize = FD_DEFAULT_CACHESIZE;
+
+ /* fd cache size that will be used if/when ih_UseLargeCache()
+ * is called */
+ vol_io_params.fd_max_cachesize = FD_MAX_CACHESIZE;
+}
#ifdef AFS_PTHREAD_ENV
/* Initialize the global ihandle mutex */
void
-ih_glock_init()
+ih_glock_init(void)
{
assert(pthread_mutex_init(&ih_glock_mutex, NULL) == 0);
}
DLL_INIT_LIST(ihashTable[i].ihash_head, ihashTable[i].ihash_tail);
}
#if defined(AFS_NT40_ENV)
- fdMaxCacheSize = FD_MAX_CACHESIZE;
+ fdMaxCacheSize = vol_io_params.fd_max_cachesize;
#elif defined(AFS_SUN5_ENV) || defined(AFS_NBSD_ENV)
{
struct rlimit rlim;
assert(getrlimit(RLIMIT_NOFILE, &rlim) == 0);
rlim.rlim_cur = rlim.rlim_max;
assert(setrlimit(RLIMIT_NOFILE, &rlim) == 0);
- fdMaxCacheSize = rlim.rlim_cur - FD_HANDLE_SETASIDE;
+ fdMaxCacheSize = rlim.rlim_cur - vol_io_params.fd_handle_setaside;
#ifdef AFS_NBSD_ENV
/* XXX this is to avoid using up all system fd netbsd is
* somewhat broken and have set maximum fd for a root process
*/
fdMaxCacheSize /= 4;
#endif
- fdMaxCacheSize = MIN(fdMaxCacheSize, FD_MAX_CACHESIZE);
+ fdMaxCacheSize = MIN(fdMaxCacheSize, vol_io_params.fd_max_cachesize);
assert(fdMaxCacheSize > 0);
}
#elif defined(AFS_HPUX_ENV)
fdMaxCacheSize = 0;
#else
{
- long fdMax = MAX(sysconf(_SC_OPEN_MAX) - FD_HANDLE_SETASIDE, 0);
- fdMaxCacheSize = (int)MIN(fdMax, FD_MAX_CACHESIZE);
+ long fdMax = MAX(sysconf(_SC_OPEN_MAX) - vol_io_params.fd_handle_setaside,
+ 0);
+ fdMaxCacheSize = (int)MIN(fdMax, vol_io_params.fd_max_cachesize);
}
#endif
- fdCacheSize = MIN(fdMaxCacheSize, FD_DEFAULT_CACHESIZE);
+ fdCacheSize = MIN(fdMaxCacheSize, vol_io_params.fd_initial_cachesize);
+
+ {
+#ifdef AFS_PTHREAD_ENV
+ pthread_t syncer;
+ pthread_attr_t tattr;
+
+ pthread_attr_init(&tattr);
+ pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
+
+ pthread_create(&syncer, &tattr, ih_sync_thread, NULL);
+#else /* AFS_PTHREAD_ENV */
+ PROCESS syncer;
+ LWP_CreateProcess(ih_sync_thread, 16*1024, LWP_MAX_PRIORITY - 2,
+ NULL, "ih_syncer", &syncer);
+#endif /* AFS_PTHREAD_ENV */
+ }
+
}
/* Make the file descriptor cache as big as possible. Don't this call
- * if the program uses fopen or fdopen. */
+ * if the program uses fopen or fdopen, if fd_max_cachesize cannot be
+ * represented in the fileno member of the system FILE structure (or
+ * equivalent).
+ */
void
ih_UseLargeCache(void)
{
IH_LOCK;
+
+ if (!ih_PkgDefaultsSet) {
+ ih_PkgDefaults();
+ }
+
if (!ih_Inited) {
- ih_Initialize();
+ ih_Initialize();
}
+
fdCacheSize = fdMaxCacheSize;
IH_UNLOCK;
int ihash = IH_HASH(dev, vid, ino);
IHandle_t *ihP;
+ if (!ih_PkgDefaultsSet) {
+ ih_PkgDefaults();
+ }
+
IH_LOCK;
if (!ih_Inited) {
- ih_Initialize();
+ ih_Initialize();
}
/* Do we already have a handle for this Inode? */
ihP->ih_vid = vid;
ihP->ih_ino = ino;
ihP->ih_flags = 0;
+ ihP->ih_synced = 0;
ihP->ih_refcnt = 1;
DLL_INIT_LIST(ihP->ih_fdhead, ihP->ih_fdtail);
DLL_INSERT_TAIL(ihP, ihashTable[ihash].ihash_head,
*/
fdInUseCount += 1;
IH_UNLOCK;
+ih_open_retry:
fd = OS_IOPEN(ihP);
IH_LOCK;
- if (fd == INVALID_FD) {
+ if (fd == INVALID_FD && (errno != EMFILE || fdLruHead == NULL) ) {
fdInUseCount -= 1;
IH_UNLOCK;
return NULL;
* we permit the number of open files to exceed fdCacheSize.
* We only recycle open file descriptors when the number
* of open files reaches the size of the cache */
- if (fdInUseCount > fdCacheSize && fdLruHead != NULL) {
+ if ((fdInUseCount > fdCacheSize || fd == INVALID_FD) && fdLruHead != NULL) {
fdP = fdLruHead;
assert(fdP->fd_status == FD_HANDLE_OPEN);
DLL_DELETE(fdP, fdLruHead, fdLruTail, fd_next, fd_prev);
DLL_DELETE(fdP, fdP->fd_ih->ih_fdhead, fdP->fd_ih->ih_fdtail,
fd_ihnext, fd_ihprev);
closeFd = fdP->fd_fd;
+ if (fd == INVALID_FD) {
+ fdCacheSize--; /* reduce in order to not run into here too often */
+ DLL_INSERT_TAIL(fdP, fdAvailHead, fdAvailTail, fd_next, fd_prev);
+ fdP->fd_status = FD_HANDLE_AVAIL;
+ fdP->fd_ih = NULL;
+ fdP->fd_fd = INVALID_FD;
+ IH_UNLOCK;
+ OS_CLOSE(closeFd);
+ goto ih_open_retry;
+ }
} else {
if (fdAvailHead == NULL) {
fdHandleAllocateChunk();
StreamHandle_t *
stream_open(const char *filename, const char *mode)
{
- FD_t fd;
+ FD_t fd = INVALID_FD;
if (strcmp(mode, "r") == 0) {
fd = OS_OPEN(filename, O_RDONLY, 0);
return 0;
IH_LOCK;
+ ihP->ih_refcnt++; /* must not disappear over unlock */
+ if (ihP->ih_synced) {
+ FdHandle_t *fdP;
+ IH_UNLOCK;
+
+ fdP = IH_OPEN(ihP);
+ if (fdP) {
+ OS_SYNC(fdP->fd_fd);
+ FDH_CLOSE(fdP);
+ }
+
+ IH_LOCK;
+ }
+
assert(ihP->ih_refcnt > 0);
+ ihP->ih_synced = 0;
+
ih_fdclose(ihP);
- IH_UNLOCK;
+ if (ihP->ih_refcnt > 1) {
+ ihP->ih_refcnt--;
+ IH_UNLOCK;
+ } else {
+ IH_UNLOCK;
+ ih_release(ihP);
+ }
return 0;
}
return code;
}
+void
+ih_sync_all(void) {
+
+ int ihash;
+
+ IH_LOCK;
+ for (ihash = 0; ihash < I_HANDLE_HASH_SIZE; ihash++) {
+ IHandle_t *ihP, *ihPnext;
+
+ ihP = ihashTable[ihash].ihash_head;
+ if (ihP)
+ ihP->ih_refcnt++; /* must not disappear over unlock */
+ for (; ihP; ihP = ihPnext) {
+
+ if (ihP->ih_synced) {
+ FdHandle_t *fdP;
+
+ ihP->ih_synced = 0;
+ IH_UNLOCK;
+
+ fdP = IH_OPEN(ihP);
+ if (fdP) {
+ OS_SYNC(fdP->fd_fd);
+ FDH_CLOSE(fdP);
+ }
+
+ IH_LOCK;
+ }
+
+ /* when decrementing the refcount, the ihandle might disappear
+ and we might not even be able to proceed to the next one.
+ Hence the gymnastics putting a hold on the next one already */
+ ihPnext = ihP->ih_next;
+ if (ihPnext) ihPnext->ih_refcnt++;
+
+ if (ihP->ih_refcnt > 1) {
+ ihP->ih_refcnt--;
+ } else {
+ IH_UNLOCK;
+ ih_release(ihP);
+ IH_LOCK;
+ }
+
+ }
+ }
+ IH_UNLOCK;
+}
+
+void *
+ih_sync_thread(void *dummy) {
+ while(1) {
+
+#ifdef AFS_PTHREAD_ENV
+ sleep(10);
+#else /* AFS_PTHREAD_ENV */
+ IOMGR_Sleep(60);
+#endif /* AFS_PTHREAD_ENV */
+
+ ih_sync_all();
+ }
+ return NULL;
+}
/*************************************************************************