vol: Add interfaces for registering RX calls
authorAndrew Deason <adeason@sinenomine.net>
Fri, 29 Oct 2010 16:14:49 +0000 (11:14 -0500)
committerDerrick Brashear <shadow@dementia.org>
Mon, 15 Nov 2010 17:31:06 +0000 (09:31 -0800)
Add VGetVolumeWithCall and VPutVolumeWithCall, to associate RX calls
with volume heavyweight references. Also add the interrupt_rxcall
field to the volume package options structure.

This also adds the VIsGoingOffline function, so a caller can tell when
a volume is going offline.

Change-Id: Iacb7738775c8e3aa611360320ca739f5de4ba625
Reviewed-on: http://gerrit.openafs.org/3215
Reviewed-by: Derrick Brashear <shadow@dementia.org>
Tested-by: BuildBot <buildbot@rampaginggeek.com>

src/vol/volume.c
src/vol/volume.h
src/vol/volume_inline.h

index 2a73c3c..370dc70 100644 (file)
@@ -511,6 +511,8 @@ VOptDefaults(ProgramType pt, VolumePackageOptions *opts)
     opts->canUseFSSYNC = 0;
     opts->canUseSALVSYNC = 0;
 
+    opts->interrupt_rxcall = NULL;
+
 #ifdef FAST_RESTART
     opts->unsafe_attach = 1;
 #else /* !FAST_RESTART */
@@ -937,6 +939,7 @@ VInitVolumePackageThread(void *args)
             vp->partition = partition;
             vp->hashid = vid;
             queue_Init(&vp->vnode_list);
+            queue_Init(&vp->rx_call_list);
            CV_INIT(&V_attachCV(vp), "partattach", CV_DEFAULT, 0);
 
             vb->batch[vb->size++] = vp;
@@ -2169,6 +2172,7 @@ VPreAttachVolumeByVp_r(Error * ec,
        osi_Assert(vp != NULL);
        memset(vp, 0, sizeof(Volume));
        queue_Init(&vp->vnode_list);
+       queue_Init(&vp->rx_call_list);
        CV_INIT(&V_attachCV(vp), "vp attach", CV_DEFAULT, 0);
     }
 
@@ -2398,6 +2402,7 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
       vp->device = partp->device;
       vp->partition = partp;
       queue_Init(&vp->vnode_list);
+      queue_Init(&vp->rx_call_list);
 #ifdef AFS_DEMAND_ATTACH_FS
       CV_INIT(&V_attachCV(vp), "vp attach", CV_DEFAULT, 0);
 #endif /* AFS_DEMAND_ATTACH_FS */
@@ -3048,8 +3053,8 @@ attach_check_vop(Error *ec, VolumeId volid, struct DiskPartition64 *partp,
  * @param[in] path     full path to the volume header .vol file
  * @param[in] partp    disk partition object for the attaching partition
  * @param[in] vp       volume object; vp->hashid, vp->device, vp->partition,
- *                     vp->vnode_list, and V_attachCV (for DAFS) should already
- *                     be initialized
+ *                     vp->vnode_list, vp->rx_call_list, and V_attachCV (for
+ *                     DAFS) should already be initialized
  * @param[in] isbusy   1 if vp->specialStatus should be set to VBUSY; that is,
  *                     if there is a volume operation running for this volume
  *                     that should set the volume to VBUSY during its run. 0
@@ -3554,6 +3559,113 @@ VHold(Volume * vp)
 }
 #endif
 
+static afs_int32
+VIsGoingOffline_r(struct Volume *vp)
+{
+    afs_int32 code = 0;
+
+    if (vp->goingOffline) {
+       if (vp->specialStatus) {
+           code = vp->specialStatus;
+       } else if (V_inService(vp) == 0 || V_blessed(vp) == 0) {
+           code = VNOVOL;
+       } else {
+           code = VOFFLINE;
+       }
+    }
+
+    return code;
+}
+
+/**
+ * Tell the caller if a volume is waiting to go offline.
+ *
+ * @param[in] vp  The volume we want to know about
+ *
+ * @return volume status
+ *   @retval 0 volume is not waiting to go offline, go ahead and use it
+ *   @retval nonzero volume is waiting to offline, and give the returned code
+ *           as an error to anyone accessing the volume
+ *
+ * @pre VOL_LOCK is NOT held
+ * @pre caller holds a heavyweight reference on vp
+ */
+afs_int32
+VIsGoingOffline(struct Volume *vp)
+{
+    afs_int32 code;
+
+    VOL_LOCK;
+    code = VIsGoingOffline_r(vp);
+    VOL_UNLOCK;
+
+    return code;
+}
+
+/**
+ * Register an RX call with a volume.
+ *
+ * @param[inout] ec        Error code; if unset when passed in, may be set if
+ *                         the volume starts going offline
+ * @param[out]   client_ec @see GetVolume
+ * @param[in] vp   Volume struct
+ * @param[in] cbv  VCallByVol struct containing the RX call to register
+ *
+ * @pre VOL_LOCK held
+ * @pre caller holds heavy ref on vp
+ *
+ * @internal
+ */
+static void
+VRegisterCall_r(Error *ec, Error *client_ec, Volume *vp, struct VCallByVol *cbv)
+{
+    if (vp && cbv) {
+#ifdef AFS_DEMAND_ATTACH_FS
+       if (!*ec) {
+           /* just in case the volume started going offline after we got the
+            * reference to it... otherwise, if the volume started going
+            * offline right at the end of GetVolume(), we might race with the
+            * RX call scanner, and return success and add our cbv to the
+            * rx_call_list _after_ the scanner has scanned the list. */
+           *ec = VIsGoingOffline_r(vp);
+           if (client_ec) {
+               *client_ec = *ec;
+           }
+       }
+
+       while (V_attachState(vp) == VOL_STATE_SCANNING_RXCALLS) {
+           VWaitStateChange_r(vp);
+       }
+#endif /* AFS_DEMAND_ATTACH_FS */
+
+       queue_Prepend(&vp->rx_call_list, cbv);
+    }
+}
+
+/**
+ * Deregister an RX call with a volume.
+ *
+ * @param[in] vp   Volume struct
+ * @param[in] cbv  VCallByVol struct containing the RX call to deregister
+ *
+ * @pre VOL_LOCK held
+ * @pre caller holds heavy ref on vp
+ *
+ * @internal
+ */
+static void
+VDeregisterCall_r(Volume *vp, struct VCallByVol *cbv)
+{
+    if (cbv && queue_IsOnQueue(cbv)) {
+#ifdef AFS_DEMAND_ATTACH_FS
+       while (V_attachState(vp) == VOL_STATE_SCANNING_RXCALLS) {
+           VWaitStateChange_r(vp);
+       }
+#endif /* AFS_DEMAND_ATTACH_FS */
+
+       queue_Remove(cbv);
+    }
+}
 
 /***************************************************/
 /* get and put volume routines                     */
@@ -3598,6 +3710,22 @@ VPutVolume(Volume * vp)
     VOL_UNLOCK;
 }
 
+/**
+ * Puts a volume reference obtained with VGetVolumeWithCall.
+ *
+ * @param[in] vp  Volume struct
+ * @param[in] cbv VCallByVol struct given to VGetVolumeWithCall, or NULL if none
+ *
+ * @pre VOL_LOCK is NOT held
+ */
+void
+VPutVolumeWithCall(Volume *vp, struct VCallByVol *cbv)
+{
+    VOL_LOCK;
+    VDeregisterCall_r(vp, cbv);
+    VPutVolume_r(vp);
+    VOL_UNLOCK;
+}
 
 /* Get a pointer to an attached volume.  The pointer is returned regardless
    of whether or not the volume is in service or on/off line.  An error
@@ -3626,6 +3754,39 @@ VGetVolumeTimed(Error * ec, Error * client_ec, VolId volumeId,
     return retVal;
 }
 
+/**
+ * Get a volume reference associated with an RX call.
+ *
+ * @param[out] ec @see GetVolume
+ * @param[out] client_ec @see GetVolume
+ * @param[in] volumeId @see GetVolume
+ * @param[in] ts  How long to wait for going-offline volumes (absolute time).
+ *                If NULL, wait forever. If ts->tv_sec == 0, return immediately
+ *                with an error if the volume is going offline.
+ * @param[in] cbv Contains an RX call to be associated with this volume
+ *                reference. This call may be interrupted if the volume is
+ *                requested to go offline while we hold a ref on it. Give NULL
+ *                to not associate an RX call with this reference.
+ *
+ * @return @see GetVolume
+ *
+ * @note for LWP builds, ts must be NULL
+ *
+ * @note A reference obtained with this function MUST be put back with
+ *       VPutVolumeWithCall
+ */
+Volume *
+VGetVolumeWithCall(Error * ec, Error * client_ec, VolId volumeId,
+                   const struct timespec *ts, struct VCallByVol *cbv)
+{
+    Volume *retVal;
+    VOL_LOCK;
+    retVal = GetVolume(ec, client_ec, volumeId, NULL, ts);
+    VRegisterCall_r(ec, client_ec, retVal, cbv);
+    VOL_UNLOCK;
+    return retVal;
+}
+
 Volume *
 VGetVolume_r(Error * ec, VolId volumeId)
 {
index 9f43b54..aa6b692 100644 (file)
@@ -206,9 +206,11 @@ typedef enum {
     VOL_STATE_SALVAGE_REQ       = 21,   /**< volume has been requested to be salvaged,
                                          *   but is waiting for other users to go away
                                          *   so it can be offlined */
+    VOL_STATE_SCANNING_RXCALLS  = 22,   /**< volume is scanning vp->rx_call_list
+                                         *   to interrupt RX calls */
     /* please add new states directly above this line */
-    VOL_STATE_FREED             = 22,   /**< debugging aid */
-    VOL_STATE_COUNT             = 23    /**< total number of valid states */
+    VOL_STATE_FREED             = 23,   /**< debugging aid */
+    VOL_STATE_COUNT             = 24    /**< total number of valid states */
 } VolState;
 
 /**
@@ -280,6 +282,9 @@ typedef struct VolumePackageOptions {
     afs_int32 canUseSALVSYNC;     /**< can we use the SALVSYNC channel? (DAFS) */
     afs_int32 unsafe_attach;      /**< can we bypass checking the inUse vol
                                    *   header on attach? */
+    void (*interrupt_rxcall) (struct rx_call *call, afs_int32 error);
+                                  /**< callback to interrupt RX calls accessing
+                                   *   a going-offline volume */
 } VolumePackageOptions;
 
 /* Magic numbers and version stamps for each type of file */
@@ -654,6 +659,14 @@ typedef struct VolumeVLRUState {
 } VolumeVLRUState;
 #endif /* AFS_DEMAND_ATTACH_FS */
 
+/**
+ * node for a volume's rx_call_list.
+ */
+struct VCallByVol {
+    struct rx_queue q;
+    struct rx_call *call;
+};
+
 typedef struct Volume {
     struct rx_queue q;          /* Volume hash chain pointers */
     VolumeId hashid;           /* Volume number -- for hash table lookup */
@@ -703,6 +716,8 @@ typedef struct Volume {
                                 * volume list--the list of volumes that will be
                                 * salvaged should the file server crash */
     struct rx_queue vnode_list; /**< linked list of cached vnodes for this volume */
+    struct rx_queue rx_call_list; /**< linked list of split RX calls using this
+                                   *   volume (fileserver only) */
 #ifdef AFS_DEMAND_ATTACH_FS
     VolState attach_state;      /* what stage of attachment has been completed */
     afs_uint32 attach_flags;    /* flags related to attachment state */
@@ -800,8 +815,11 @@ extern char *VSalvageMessage;      /* Canonical message when a volume is forced
 extern Volume *VGetVolume(Error * ec, Error * client_ec, VolId volumeId);
 extern Volume *VGetVolumeTimed(Error * ec, Error * client_ec, VolId volumeId,
                                const struct timespec *ts);
+extern Volume *VGetVolumeWithCall(Error * ec, Error * client_ec, VolId volumeId,
+                                  const struct timespec *ts, struct VCallByVol *cbv);
 extern Volume *VGetVolume_r(Error * ec, VolId volumeId);
 extern void VPutVolume(Volume *);
+extern void VPutVolumeWithCall(Volume *vp, struct VCallByVol *cbv);
 extern void VPutVolume_r(Volume *);
 extern void VOffline(Volume * vp, char *message);
 extern void VOffline_r(Volume * vp, char *message);
@@ -861,6 +879,7 @@ extern Volume * VLookupVolume_r(Error * ec, VolId volumeId, Volume * hint);
 extern void VGetVolumePath(Error * ec, VolId volumeId, char **partitionp,
                           char **namep);
 extern char *vol_DevName(dev_t adev, char *wpath);
+extern afs_int32 VIsGoingOffline(struct Volume *vp);
 
 struct VLockFile;
 extern void VLockFileInit(struct VLockFile *lf, const char *path);
index 7b75a7b..b270a00 100644 (file)
@@ -299,6 +299,7 @@ VIsExclusiveState(VolState state)
     case VOL_STATE_VNODE_CLOSE:
     case VOL_STATE_VNODE_RELEASE:
     case VOL_STATE_VLRU_ADD:
+    case VOL_STATE_SCANNING_RXCALLS:
        return 1;
     default:
        return 0;