Modify the NFSv4.1 server so that it allows ReclaimComplete as done by ESXi 6.7.

I believe that a ReclaimComplete with rca_one_fs == TRUE is only
to be used after a file system has been transferred to a different
file server.  However, RFC5661 is somewhat vague w.r.t. this and
the ESXi 6.7 client does both a ReclaimComplete with rca_one_fs == TRUE
and one with ReclaimComplete with rca_one_fs == FALSE.
Therefore, just ignore the rca_one_fs == TRUE operation and return
NFS_OK without doing anything instead of replying NFS4ERR_NOTSUPP.
This allows the ESXi 6.7 NFSv4.1 client to do a mount.
After discussion on the NFSv4 IETF working group mailing list, doing this
along with setting a flag to note that a ReclaimComplete with rca_one_fs TRUE
was an appropriate way to handle this.
The flag that indicates that a ReclaimComplete with rca_one_fs == TRUE was
done may be used to disable replies of NFS4ERR_GRACE for non-reclaim
state operations in a future commit.

This patch along with r332790, r334492 and r336357 allow ESXi 6.7 NFSv4.1 mounts
work ok. ESX 6.5 NFSv4.1 mounts do not work well, due to what I believe are
violations of RFC-5661 and should not be used.

Reported by:	andreas.nagy@frequentis.com
Tested by:	andreas.nagy@frequentis.com, daniel@ftml.net (earlier version)
MFC after:	2 weeks
Relnotes:	yes
This commit is contained in:
Rick Macklem 2018-07-28 20:21:04 +00:00
parent 80d3469a3f
commit a3e709cd33
4 changed files with 20 additions and 8 deletions

View File

@ -329,6 +329,7 @@ struct nfsreferral {
#define LCL_RECLAIMCOMPLETE 0x00010000
#define LCL_NFSV41 0x00020000
#define LCL_DONEBINDCONN 0x00040000
#define LCL_RECLAIMONEFS 0x00080000
#define LCL_GSS LCL_KERBV /* Or of all mechs */

View File

@ -141,7 +141,7 @@ void nfsrv_nfsuserddelport(void);
void nfsrv_throwawayallstate(NFSPROC_T *);
int nfsrv_checksequence(struct nfsrv_descript *, uint32_t, uint32_t *,
uint32_t *, int, uint32_t *, NFSPROC_T *);
int nfsrv_checkreclaimcomplete(struct nfsrv_descript *);
int nfsrv_checkreclaimcomplete(struct nfsrv_descript *, int);
void nfsrv_cache_session(uint8_t *, uint32_t, int, struct mbuf **);
void nfsrv_freeallbackchannel_xprts(void);
int nfsrv_layoutcommit(struct nfsrv_descript *, vnode_t, int, int, uint64_t,

View File

@ -4229,17 +4229,26 @@ nfsrvd_reclaimcomplete(struct nfsrv_descript *nd, __unused int isdgram,
__unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
{
uint32_t *tl;
int error = 0;
int error = 0, onefs;
if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
nd->nd_repstat = NFSERR_WRONGSEC;
goto nfsmout;
}
NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
/*
* I believe that a ReclaimComplete with rca_one_fs == TRUE is only
* to be used after a file system has been transferred to a different
* file server. However, RFC5661 is somewhat vague w.r.t. this and
* the ESXi 6.7 client does both a ReclaimComplete with rca_one_fs
* == TRUE and one with ReclaimComplete with rca_one_fs == FALSE.
* Therefore, just ignore the rca_one_fs == TRUE operation and return
* NFS_OK without doing anything.
*/
onefs = 0;
if (*tl == newnfs_true)
nd->nd_repstat = NFSERR_NOTSUPP;
else
nd->nd_repstat = nfsrv_checkreclaimcomplete(nd);
onefs = 1;
nd->nd_repstat = nfsrv_checkreclaimcomplete(nd, onefs);
nfsmout:
NFSEXITCODE2(error, nd);
return (error);

View File

@ -6064,7 +6064,7 @@ nfsrv_checksequence(struct nfsrv_descript *nd, uint32_t sequenceid,
* Check/set reclaim complete for this session/clientid.
*/
int
nfsrv_checkreclaimcomplete(struct nfsrv_descript *nd)
nfsrv_checkreclaimcomplete(struct nfsrv_descript *nd, int onefs)
{
struct nfsdsession *sep;
struct nfssessionhash *shp;
@ -6080,8 +6080,10 @@ nfsrv_checkreclaimcomplete(struct nfsrv_descript *nd)
return (NFSERR_BADSESSION);
}
/* Check to see if reclaim complete has already happened. */
if ((sep->sess_clp->lc_flags & LCL_RECLAIMCOMPLETE) != 0)
if (onefs != 0)
sep->sess_clp->lc_flags |= LCL_RECLAIMONEFS;
/* Check to see if reclaim complete has already happened. */
else if ((sep->sess_clp->lc_flags & LCL_RECLAIMCOMPLETE) != 0)
error = NFSERR_COMPLETEALREADY;
else {
sep->sess_clp->lc_flags |= LCL_RECLAIMCOMPLETE;