vol, volser, and viced type fixes
[openafs.git] / src / vol / ihandle.c
index d265059..2ea3fb7 100644 (file)
 #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
@@ -31,13 +30,7 @@ RCSID
 #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>
@@ -84,11 +77,16 @@ FdHandle_t *fdLruHead;
 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;
 
@@ -98,11 +96,34 @@ int fdInUseCount = 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);
 }
@@ -122,14 +143,14 @@ ih_Initialize(void)
        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
@@ -141,7 +162,7 @@ ih_Initialize(void)
         */
        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)
@@ -149,22 +170,49 @@ ih_Initialize(void)
     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;
@@ -193,9 +241,13 @@ ih_init(int dev, int vid, Inode ino)
     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? */
@@ -218,6 +270,7 @@ ih_init(int dev, int vid, Inode ino)
     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,
@@ -307,9 +360,10 @@ ih_open(IHandle_t * ihP)
      */
     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;
@@ -319,13 +373,23 @@ ih_open(IHandle_t * ihP)
      * 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();
@@ -483,7 +547,7 @@ stream_fdopen(FD_t fd)
 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);
@@ -612,7 +676,7 @@ stream_write(void *ptr, afs_fsize_t size, afs_fsize_t nitems,
 int
 stream_seek(StreamHandle_t * streamP, afs_foff_t offset, int whence)
 {
-    int rc;
+    ssize_t rc;
     int retval = 0;
 
     if (streamP->str_direction == STREAM_DIRECTION_WRITE
@@ -639,7 +703,7 @@ stream_seek(StreamHandle_t * streamP, afs_foff_t offset, int whence)
 int
 stream_flush(StreamHandle_t * streamP)
 {
-    int rc;
+    ssize_t rc;
     int retval = 0;
 
     if (streamP->str_direction == STREAM_DIRECTION_WRITE
@@ -661,7 +725,7 @@ stream_flush(StreamHandle_t * streamP)
 int
 stream_close(StreamHandle_t * streamP, int reallyClose)
 {
-    int rc;
+    ssize_t rc;
     int retval = 0;
 
     assert(streamP != NULL);
@@ -779,10 +843,32 @@ ih_reallyclose(IHandle_t * ihP)
        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;
 }
 
@@ -840,6 +926,68 @@ ih_condsync(IHandle_t * ihP)
     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;
+}
 
 
 /*************************************************************************