vol, volser, and viced type fixes
[openafs.git] / src / vol / vnode.c
index 7cb6e06..a5f6957 100644 (file)
 #include <afs/param.h>
 #define MAXINT     (~(1<<((sizeof(int)*8)-1)))
 
-RCSID
-    ("$Header$");
 
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
+#include <stdarg.h>
 #ifdef AFS_PTHREAD_ENV
 #include <assert.h>
 #else /* AFS_PTHREAD_ENV */
@@ -65,6 +64,10 @@ RCSID
 #endif /* AFS_NT40_ENV */
 #include <sys/stat.h>
 
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+
 /*@printflike@*/ extern void Log(const char *format, ...);
 
 /*@printflike@*/ extern void Abort(const char *format, ...);
@@ -72,8 +75,7 @@ RCSID
 
 struct VnodeClassInfo VnodeClassInfo[nVNODECLASSES];
 
-private void StickOnLruChain_r(register Vnode * vnp,
-                              register struct VnodeClassInfo *vcp);
+void VNLog(afs_int32 aop, afs_int32 anparms, ... );
 
 extern int LogLevel;
 
@@ -109,17 +111,13 @@ extern int LogLevel;
 static afs_int32 theLog[THELOGSIZE];
 static afs_int32 vnLogPtr = 0;
 void
-VNLog(afs_int32 aop, afs_int32 anparms, afs_int32 av1, afs_int32 av2, 
-      afs_int32 av3, afs_int32 av4)
+VNLog(afs_int32 aop, afs_int32 anparms, ... )
 {
     register afs_int32 temp;
-    afs_int32 data[4];
+    va_list ap;
+
+    va_start(ap, anparms);
 
-    /* copy data to array */
-    data[0] = av1;
-    data[1] = av2;
-    data[2] = av3;
-    data[3] = av4;
     if (anparms > 4)
        anparms = 4;            /* do bounds checking */
 
@@ -128,10 +126,11 @@ VNLog(afs_int32 aop, afs_int32 anparms, afs_int32 av1, afs_int32 av2,
     if (vnLogPtr >= THELOGSIZE)
        vnLogPtr = 0;
     for (temp = 0; temp < anparms; temp++) {
-       theLog[vnLogPtr++] = data[temp];
+       theLog[vnLogPtr++] = va_arg(ap, afs_int32);
        if (vnLogPtr >= THELOGSIZE)
            vnLogPtr = 0;
     }
+    va_end(ap);
 }
 
 /* VolumeHashOffset -- returns a new value to be stored in the
@@ -486,7 +485,7 @@ VGetFreeVnode_r(struct VnodeClassInfo * vcp)
     if (Vn_refcount(vnp) != 0 || CheckLock(&vnp->lock))
        Abort("VGetFreeVnode_r: locked vnode in lruq");
 #endif
-    VNLog(1, 2, Vn_id(vnp), (afs_int32) vnp, 0, 0);
+    VNLog(1, 2, Vn_id(vnp), (intptr_t)vnp, 0, 0);
 
     /* 
      * it's going to be overwritten soon enough.
@@ -506,6 +505,14 @@ VGetFreeVnode_r(struct VnodeClassInfo * vcp)
        VnChangeState_r(vnp, VN_STATE_RELEASING);
        VOL_UNLOCK;
 #endif
+       /* release is, potentially, a highly latent operation due to a couple
+        * factors:
+        *   - ihandle package lock contention
+        *   - closing file descriptor(s) associated with ih
+        *
+        * Hance, we perform outside of the volume package lock in order to 
+        * reduce the probability of contention.
+        */
        IH_RELEASE(vnp->handle);
 #ifdef AFS_DEMAND_ATTACH_FS
        VOL_LOCK;
@@ -584,7 +591,7 @@ VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
 {
     register Vnode *vnp;
     VnodeId vnodeNumber;
-    int bitNumber, code;
+    int bitNumber;
     register struct VnodeClassInfo *vcp;
     VnodeClass class;
     Unique unique;
@@ -659,7 +666,7 @@ VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
     if (vnp) {
        /* slot already exists.  May even not be in lruq (consider store file locking a file being deleted)
         * so we may have to wait for it below */
-       VNLog(3, 2, vnodeNumber, (afs_int32) vnp, 0, 0);
+       VNLog(3, 2, vnodeNumber, (intptr_t)vnp, 0, 0);
 
        VnCreateReservation_r(vnp);
        if (Vn_refcount(vnp) == 1) {
@@ -816,13 +823,13 @@ VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
 
        }
     sane:
-       VNLog(4, 2, vnodeNumber, (afs_int32) vnp, 0, 0);
+       VNLog(4, 2, vnodeNumber, (intptr_t)vnp, 0, 0);
 #ifndef AFS_DEMAND_ATTACH_FS
        AddToVnHash(vnp);
 #endif
     }
 
-    VNLog(5, 1, (afs_int32) vnp, 0, 0, 0);
+    VNLog(5, 1, (intptr_t)vnp, 0, 0, 0);
     memset(&vnp->disk, 0, sizeof(vnp->disk));
     vnp->changed_newTime = 0;  /* set this bit when vnode is updated */
     vnp->changed_oldTime = 0;  /* set this on CopyOnWrite. */
@@ -863,7 +870,8 @@ VnLoad(Error * ec, Volume * vp, Vnode * vnp,
 {
     /* vnode not cached */
     Error error;
-    int n, dosalv = 1;
+    int dosalv = 1;
+    ssize_t nBytes;
     IHandle_t *ihP = vp->vnodeIndex[class].handle;
     FdHandle_t *fdP;
 
@@ -889,23 +897,23 @@ VnLoad(Error * ec, Volume * vp, Vnode * vnp,
        Log("VnLoad: can't seek on index file vn=%u\n", Vn_id(vnp));
        *ec = VIO;
        goto error_encountered_nolock;
-    } else if ((n = FDH_READ(fdP, (char *)&vnp->disk, vcp->diskSize))
+    } else if ((nBytes = FDH_READ(fdP, (char *)&vnp->disk, vcp->diskSize))
               != vcp->diskSize) {
        /* Don't take volume off line if the inumber is out of range
         * or the inode table is full. */
-       if (n == BAD_IGET) {
+       if (nBytes == BAD_IGET) {
            Log("VnLoad: bad inumber %s\n",
                PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
            *ec = VIO;
            dosalv = 0;
-       } else if (n == -1 && errno == EIO) {
+       } else if (nBytes == -1 && errno == EIO) {
            /* disk error; salvage */
            Log("VnLoad: Couldn't read vnode %u, volume %u (%s); volume needs salvage\n", Vn_id(vnp), V_id(vp), V_name(vp));
        } else {
            /* vnode is not allocated */
            if (LogLevel >= 5) 
                Log("VnLoad: Couldn't read vnode %u, volume %u (%s); read %d bytes, errno %d\n", 
-                   Vn_id(vnp), V_id(vp), V_name(vp), n, errno);
+                   Vn_id(vnp), V_id(vp), V_name(vp), (int)nBytes, errno);
            *ec = VIO;
            dosalv = 0;
        }
@@ -989,7 +997,8 @@ static void
 VnStore(Error * ec, Volume * vp, Vnode * vnp, 
        struct VnodeClassInfo * vcp, VnodeClass class)
 {
-    int offset, code;
+    ssize_t nBytes;
+    afs_foff_t offset;
     IHandle_t *ihP = vp->vnodeIndex[class].handle;
     FdHandle_t *fdP;
 #ifdef AFS_DEMAND_ATTACH_FS
@@ -1015,13 +1024,13 @@ VnStore(Error * ec, Volume * vp, Vnode * vnp,
        goto error_encountered;
     }
 
-    code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
-    if (code != vcp->diskSize) {
+    nBytes = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
+    if (nBytes != vcp->diskSize) {
        /* Don't force volume offline if the inumber is out of
         * range or the inode table is full.
         */
        FDH_REALLYCLOSE(fdP);
-       if (code == BAD_IGET) {
+       if (nBytes == BAD_IGET) {
            Log("VnStore: bad inumber %s\n",
                PrintInode(NULL,
                           vp->vnodeIndex[class].handle->ih_ino));
@@ -1031,7 +1040,7 @@ VnStore(Error * ec, Volume * vp, Vnode * vnp,
            VnChangeState_r(vnp, VN_STATE_ERROR);
 #endif
        } else {
-           Log("VnStore: Couldn't write vnode %u, volume %u (%s) (error %d)\n", Vn_id(vnp), V_id(Vn_volume(vnp)), V_name(Vn_volume(vnp)), code);
+           Log("VnStore: Couldn't write vnode %u, volume %u (%s) (error %d)\n", Vn_id(vnp), V_id(Vn_volume(vnp)), V_name(Vn_volume(vnp)), (int)nBytes);
 #ifdef AFS_DEMAND_ATTACH_FS
            goto error_encountered;
 #else
@@ -1106,10 +1115,8 @@ Vnode *
 VGetVnode_r(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
 {                              /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
     register Vnode *vnp;
-    int code;
     VnodeClass class;
     struct VnodeClassInfo *vcp;
-    Volume * oldvp = NULL;
 
     *ec = 0;
 
@@ -1166,7 +1173,7 @@ VGetVnode_r(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
     if (vnp) {
        /* vnode is in cache */
 
-       VNLog(101, 2, vnodeNumber, (afs_int32) vnp, 0, 0);
+       VNLog(101, 2, vnodeNumber, (intptr_t)vnp, 0, 0);
        VnCreateReservation_r(vnp);
 
 #ifdef AFS_DEMAND_ATTACH_FS
@@ -1253,7 +1260,7 @@ VGetVnode_r(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
 
     /* Check that the vnode hasn't been removed while we were obtaining
      * the lock */
-    VNLog(102, 2, vnodeNumber, (afs_int32) vnp, 0, 0);
+    VNLog(102, 2, vnodeNumber, (intptr_t) vnp, 0, 0);
     if ((vnp->disk.type == vNull) || (Vn_cacheCheck(vnp) == 0)) {
        VnUnlock(vnp, locktype);
        VnCancelReservation_r(vnp);
@@ -1309,14 +1316,13 @@ VPutVnode_r(Error * ec, register Vnode * vnp)
     int writeLocked;
     VnodeClass class;
     struct VnodeClassInfo *vcp;
-    int code;
 
     *ec = 0;
     assert(Vn_refcount(vnp) != 0);
     class = vnodeIdToClass(Vn_id(vnp));
     vcp = &VnodeClassInfo[class];
     assert(vnp->disk.vnodeMagic == vcp->magic);
-    VNLog(200, 2, Vn_id(vnp), (afs_int32) vnp, 0, 0);
+    VNLog(200, 2, Vn_id(vnp), (intptr_t) vnp, 0, 0);
 
 #ifdef AFS_DEMAND_ATTACH_FS
     writeLocked = (Vn_state(vnp) == VN_STATE_EXCLUSIVE);
@@ -1332,7 +1338,7 @@ VPutVnode_r(Error * ec, register Vnode * vnp)
        PROCESS thisProcess;
        LWP_CurrentProcess(&thisProcess);
 #endif /* AFS_PTHREAD_ENV */
-       VNLog(201, 2, (afs_int32) vnp,
+       VNLog(201, 2, (intptr_t) vnp,
              ((vnp->changed_newTime) << 1) | ((vnp->
                                                changed_oldTime) << 1) | vnp->
              delete, 0, 0);
@@ -1350,7 +1356,7 @@ VPutVnode_r(Error * ec, register Vnode * vnp)
                /* No longer any directory entries for this vnode. Free the Vnode */
                memset(&vnp->disk, 0, sizeof(vnp->disk));
                /* delete flag turned off further down */
-               VNLog(202, 2, Vn_id(vnp), (afs_int32) vnp, 0, 0);
+               VNLog(202, 2, Vn_id(vnp), (intptr_t) vnp, 0, 0);
            } else if (vnp->changed_newTime) {
                vnp->disk.serverModifyTime = now;
            }
@@ -1445,7 +1451,6 @@ VVnodeWriteToRead_r(Error * ec, register Vnode * vnp)
     int writeLocked;
     VnodeClass class;
     struct VnodeClassInfo *vcp;
-    int code;
 #ifdef AFS_PTHREAD_ENV
     pthread_t thisProcess;
 #else /* AFS_PTHREAD_ENV */
@@ -1457,7 +1462,7 @@ VVnodeWriteToRead_r(Error * ec, register Vnode * vnp)
     class = vnodeIdToClass(Vn_id(vnp));
     vcp = &VnodeClassInfo[class];
     assert(vnp->disk.vnodeMagic == vcp->magic);
-    VNLog(300, 2, Vn_id(vnp), (afs_int32) vnp, 0, 0);
+    VNLog(300, 2, Vn_id(vnp), (intptr_t) vnp, 0, 0);
 
 #ifdef AFS_DEMAND_ATTACH_FS
     writeLocked = (Vn_state(vnp) == VN_STATE_EXCLUSIVE);
@@ -1469,7 +1474,7 @@ VVnodeWriteToRead_r(Error * ec, register Vnode * vnp)
     }
 
 
-    VNLog(301, 2, (afs_int32) vnp,
+    VNLog(301, 2, (intptr_t) vnp,
          ((vnp->changed_newTime) << 1) | ((vnp->
                                            changed_oldTime) << 1) | vnp->
          delete, 0, 0);
@@ -1482,7 +1487,7 @@ VVnodeWriteToRead_r(Error * ec, register Vnode * vnp)
 #endif /* AFS_PTHREAD_ENV */
     if (thisProcess != vnp->writer)
        Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",
-             (int)vnp);
+             (intptr_t)vnp);
 
     if (vnp->delete) {
        return 0;
@@ -1507,7 +1512,6 @@ VVnodeWriteToRead_r(Error * ec, register Vnode * vnp)
        } else {
            VnStore(ec, vp, vnp, vcp, class);
        }
-    sane:
        vcp->writes++;
        vnp->changed_newTime = vnp->changed_oldTime = 0;
     }
@@ -1522,6 +1526,112 @@ VVnodeWriteToRead_r(Error * ec, register Vnode * vnp)
     return 0;
 }
 
+/** 
+ * initial size of ihandle pointer vector.
+ *
+ * @see VInvalidateVnodesByVolume_r
+ */
+#define IH_VEC_BASE_SIZE 256
+
+/**
+ * increment amount for growing ihandle pointer vector.
+ *
+ * @see VInvalidateVnodesByVolume_r
+ */
+#define IH_VEC_INCREMENT 256
+
+/**
+ * Compile list of ihandles to be released/reallyclosed at a later time.
+ *
+ * @param[in]   vp            volume object pointer
+ * @param[out]  vec_out       vector of ihandle pointers to be released/reallyclosed
+ * @param[out]  vec_len_out   number of valid elements in ihandle vector
+ *
+ * @pre - VOL_LOCK is held
+ *      - volume is in appropriate exclusive state (e.g. VOL_STATE_VNODE_CLOSE,
+ *        VOL_STATE_VNODE_RELEASE)
+ *
+ * @post - all vnodes on VVn list are invalidated
+ *       - ih_vec is populated with all valid ihandles
+ *
+ * @return operation status
+ *    @retval 0         success
+ *    @retval ENOMEM    out of memory
+ *
+ * @todo we should handle out of memory conditions more gracefully.
+ *
+ * @internal vnode package internal use only
+ */
+static int
+VInvalidateVnodesByVolume_r(Volume * vp,
+                           IHandle_t *** vec_out,
+                           size_t * vec_len_out)
+{
+    int ret = 0;
+    Vnode *vnp, *nvnp;
+    size_t i = 0, vec_len;
+    IHandle_t **ih_vec, **ih_vec_new;
+
+#ifdef AFS_DEMAND_ATTACH_FS
+    VOL_UNLOCK;
+#endif /* AFS_DEMAND_ATTACH_FS */
+
+    vec_len = IH_VEC_BASE_SIZE;
+    ih_vec = malloc(sizeof(IHandle_t *) * vec_len);
+#ifdef AFS_DEMAND_ATTACH_FS
+    VOL_LOCK;
+#endif
+    if (ih_vec == NULL)
+       return ENOMEM;
+
+    /* 
+     * Traverse the volume's vnode list.  Pull all the ihandles out into a 
+     * thread-private array for later asynchronous processing.
+     */
+#ifdef AFS_DEMAND_ATTACH_FS
+restart_traversal:
+#endif
+    for (queue_Scan(&vp->vnode_list, vnp, nvnp, Vnode)) {
+       if (vnp->handle != NULL) {
+           if (i == vec_len) {
+#ifdef AFS_DEMAND_ATTACH_FS
+               VOL_UNLOCK;
+#endif
+               vec_len += IH_VEC_INCREMENT;
+               ih_vec_new = realloc(ih_vec, sizeof(IHandle_t *) * vec_len);
+#ifdef AFS_DEMAND_ATTACH_FS
+               VOL_LOCK;
+#endif
+               if (ih_vec_new == NULL) {
+                   ret = ENOMEM;
+                   goto done;
+               }
+               ih_vec = ih_vec_new;
+#ifdef AFS_DEMAND_ATTACH_FS
+               /*
+                * Theoretically, the volume's VVn list should not change 
+                * because the volume is in an exclusive state.  For the
+                * sake of safety, we will restart the traversal from the
+                * the beginning (which is not expensive because we're
+                * deleting the items from the list as we go).
+                */
+               goto restart_traversal;
+#endif
+           }
+           ih_vec[i++] = vnp->handle;
+           vnp->handle = NULL;
+       }
+       DeleteFromVVnList(vnp);
+       VInvalidateVnode_r(vnp);
+    }
+
+ done:
+    *vec_out = ih_vec;
+    *vec_len_out = i;
+
+    return ret;
+}
+
 /* VCloseVnodeFiles - called when a volume is going off line. All open
  * files for vnodes in that volume are closed. This might be excessive,
  * since we may only be taking one volume of a volume group offline.
@@ -1529,26 +1639,43 @@ VVnodeWriteToRead_r(Error * ec, register Vnode * vnp)
 void
 VCloseVnodeFiles_r(Volume * vp)
 {
-    int i;
-    Vnode *vnp, *nvnp;
 #ifdef AFS_DEMAND_ATTACH_FS
     VolState vol_state_save;
+#endif
+    IHandle_t ** ih_vec;
+    size_t i, vec_len;
 
+#ifdef AFS_DEMAND_ATTACH_FS
     vol_state_save = VChangeState_r(vp, VOL_STATE_VNODE_CLOSE);
-    VOL_UNLOCK;
 #endif /* AFS_DEMAND_ATTACH_FS */
 
-    for (queue_Scan(&vp->vnode_list, vnp, nvnp, Vnode)) {
-       IH_REALLYCLOSE(vnp->handle);
-       DeleteFromVVnList(vnp);
+    /* XXX need better error handling here */
+    assert(VInvalidateVnodesByVolume_r(vp,
+                                      &ih_vec,
+                                      &vec_len) == 0);
+
+    /*
+     * DAFS:
+     * now we drop VOL_LOCK while we perform some potentially very
+     * expensive operations in the background
+     */
+#ifdef AFS_DEMAND_ATTACH_FS
+    VOL_UNLOCK;
+#endif
+
+    for (i = 0; i < vec_len; i++) {
+       IH_REALLYCLOSE(ih_vec[i]);
     }
 
+    free(ih_vec);
+
 #ifdef AFS_DEMAND_ATTACH_FS
     VOL_LOCK;
     VChangeState_r(vp, vol_state_save);
 #endif /* AFS_DEMAND_ATTACH_FS */
 }
 
+
 /**
  * shut down all vnode cache state for a given volume.
  *
@@ -1566,25 +1693,46 @@ VCloseVnodeFiles_r(Volume * vp)
  *       during this exclusive operation.  This is due to the fact that we are
  *       generally called during the refcount 1->0 transition.
  *
+ * @todo we should handle failures in VInvalidateVnodesByVolume_r more 
+ *       gracefully.
+ *
+ * @see VInvalidateVnodesByVolume_r
+ *
  * @internal this routine is internal to the volume package
  */
 void
 VReleaseVnodeFiles_r(Volume * vp)
 {
-    int i;
-    Vnode *vnp, *nvnp;
 #ifdef AFS_DEMAND_ATTACH_FS
     VolState vol_state_save;
+#endif
+    IHandle_t ** ih_vec;
+    size_t i, vec_len;
 
+#ifdef AFS_DEMAND_ATTACH_FS
     vol_state_save = VChangeState_r(vp, VOL_STATE_VNODE_RELEASE);
-    VOL_UNLOCK;
 #endif /* AFS_DEMAND_ATTACH_FS */
 
-    for (queue_Scan(&vp->vnode_list, vnp, nvnp, Vnode)) {
-       IH_RELEASE(vnp->handle);
-       DeleteFromVVnList(vnp);
+    /* XXX need better error handling here */
+    assert(VInvalidateVnodesByVolume_r(vp,
+                                      &ih_vec,
+                                      &vec_len) == 0);
+
+    /*
+     * DAFS:
+     * now we drop VOL_LOCK while we perform some potentially very
+     * expensive operations in the background
+     */
+#ifdef AFS_DEMAND_ATTACH_FS
+    VOL_UNLOCK;
+#endif
+
+    for (i = 0; i < vec_len; i++) {
+       IH_RELEASE(ih_vec[i]);
     }
 
+    free(ih_vec);
+
 #ifdef AFS_DEMAND_ATTACH_FS
     VOL_LOCK;
     VChangeState_r(vp, vol_state_save);