Subject: [nfsv4] re: delegation recall with CB path down

From: [email-masked]
Date: Tue, 3 May 2005 15:39:43 -0400 (EDT)

> so it seems like just because the callback path is down, the server
> should not
> revoke delegations, so in case the server finds out that the callback
> path is down because it is in the middle of recalling the delegation, it
> probably needs to wait for 
> a renew from that client so that it can send it NFS4ERR_CB_PATH_DOWN and
> then 
> wait for atleast one lease period before revoking the delegation. 
> 
> Does this seem like the right approach? Are there other server
> implementations which have implemented this approach and do current
> clients correctly handle this error case?

This is essentially what my server does. After issuing the Recall, it
sets a timeout of "2 * lease + delta" before revocation, if the recall
failed. Since the failed recall will have marked the callback path down,
it will reply NFS4ERR_CB_PATH_DOWN to any Renew during that time.
--> The client should have somewhat more than one lease duration after
    receiving the NFS4ERR_CB_PATH_DOWN to return outstanding delegations.

I've attached my code, in case the comments are of interest to anyone, rick
--- bsd delegation recall code ---
	/*
	 * If the conflict is with an old delegation...
	 */
	if (stp->ls_flags & NFSLCK_OLDDELEG) {
		/*
		 * You can delete it, if it has expired.
		 */
		if (clp->lc_delegtime < NFSD_MONOSEC) {
			s = splsoftclock();
			nfsrv_freedeleg(stp);
			splx(s);
			return (0);
		}
		/*
		 * During this delay, the old delegation could expire or it
		 * could be recovered by the client via an Open with
		 * CLAIM_DELEGATE_PREV.
		 * Release the nfsv4root_lock, if held.
		 */
		if (*haslockp) {
			*haslockp = 0;
			nfsrv_v4rootunlock(1);
		}
		return (NFSERR_DELAY);
	}

	/*
	 * It's a current delegation, so:
	 * - check to see if the delegation has expired
	 *   - if so, get the v4root lock and then expire it
	 */
	if (!(stp->ls_flags & NFSLCK_DELEGRECALL)) {
		if (*haslockp) {
			*haslockp = 0;
			nfsrv_v4rootunlock(1);
		}
		/*
		 * - do a recall callback, since not yet done
		 * For now, never allow truncate to be set. To use
		 * truncate safely, it must be guaranteed that the
		 * Remove, Rename or Setattr with size of 0 will
		 * succeed and that would require major changes to
		 * the VFS/Vnode OPs.
		 * Set the expiry time large enough so that it won't expire
		 * until after the callback, then set it correctly, once
		 * the callback is done. (The delegation will now time
		 * out whether or not the Recall worked ok, but with a
		 * larger timeout if it succeeded.)
		 */
		stp->ls_delegtime = NFSD_MONOSEC + 240;
		stp->ls_flags |= NFSLCK_DELEGRECALL;

		/*
		 * Loop NFSRV_CBRETRYCNT times while the CBRecall replies
		 * NFSERR_BADSTATEID or NFSERR_BADHANDLE. This is done
		 * in order to try and avoid a race that could happen
		 * when a CBRecall request passed the Open reply with
		 * the delegation in it when transitting the network.
		 */
		retrycnt = 0;
		do {
		    error = nfsrv_docallback(clp, NFSV4OP_CBRECALL,
			&stp->ls_stateid,0,&stp->ls_lfp->lf_fh,NULL,NULL,p);
		    retrycnt++;
		} while ((error == NFSERR_BADSTATEID ||
		    error == NFSERR_BADHANDLE) && retrycnt < NFSV4_CBRETRYCNT);
		if (error)
			stp->ls_delegtime = NFSD_MONOSEC +
				(2 * nfsrv_lease) + NFSRV_LEASEDELTA;
		else
			stp->ls_delegtime = NFSD_MONOSEC +
				(5 * nfsrv_lease) + NFSRV_LEASEDELTA;
		return (NFSERR_DELAY);
	}

	if (clp->lc_expiry >= NFSD_MONOSEC &&
	    stp->ls_delegtime >= NFSD_MONOSEC) {
		/*
		 * A recall has been done, but it has not yet expired.
		 * So, RETURN_DELAY.
		 */
		if (*haslockp) {
			*haslockp = 0;
			nfsrv_v4rootunlock(1);
		}
		return (NFSERR_DELAY);
	}

	/*
	 * If we don't yet have the lock, just get it and then return,
	 * since we need that before deleting expired state, such as
	 * this delegation.
	 * When getting the lock, unlock the vnode, so other nfsds that
	 * are in progress, won't get stuck waiting for the vnode lock.
	 */
	if (*haslockp == 0) {
		nfsrv_v4rootrelref();
		VOP_UNLOCK(vp, 0, p);
		do {
			gotlock = nfsrv_v4rootlock(1);
		} while (!gotlock);
		*haslockp = 1;
		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
		return (-1);
	}

	/*
	 * Ok, we can delete the expired delegation.
	 * First, write the Revoke record to stable storage and then
	 * clear out the conflict.
	 */
	nfsrv_writestable(clp->lc_id, clp->lc_idlen, NFSNST_REVOKE, p);
	s = splsoftclock();
	if (clp->lc_expiry < NFSD_MONOSEC) {
		nfsrv_cleanclient(clp);
		nfsrv_freedeleglist(&clp->lc_deleg);
		nfsrv_freedeleglist(&clp->lc_olddeleg);
		LIST_REMOVE(clp, lc_hash);
		zapped_clp = 1;
	} else {
		nfsrv_freedeleg(stp);
		zapped_clp = 0;
	}
	splx(s);
	if (zapped_clp)
		nfsrv_zapclient(clp, p);

_______________________________________________
nfsv4 mailing list
[email-masked]
https://www1.ietf.org/mailman/listinfo/nfsv4