summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrmacklem <rmacklem@FreeBSD.org>2017-05-08 21:29:29 +0000
committerrmacklem <rmacklem@FreeBSD.org>2017-05-08 21:29:29 +0000
commit60b322ba95cfd0dbf6eb6dc81b6ae628c1323a93 (patch)
treef0940d48bc5cd45c5a8285590050b31ba21bca4e
parent9bc82128d57d119fc66b19a41aac9c04e3729414 (diff)
downloadFreeBSD-src-60b322ba95cfd0dbf6eb6dc81b6ae628c1323a93.zip
FreeBSD-src-60b322ba95cfd0dbf6eb6dc81b6ae628c1323a93.tar.gz
MFC: r317345
Make the NFSv4 client to use a write open for reading if allowed by the server. An NFSv4 server has the option of allowing a Read to be done using a Write Open. If this is not allowed, the server will return NFSERR_OPENMODE. This patch attempts the read with a write open and then disables this if the server replies NFSERR_OPENMODE. This change will avoid some uses of the special stateids. This will be useful for pNFS/DS Reads, since they cannot use special stateids. It will also be useful for any NFSv4 server that does not support reading via the special stateids. It has been tested against both types of NFSv4 server.
-rw-r--r--sys/fs/nfs/nfsport.h2
-rw-r--r--sys/fs/nfsclient/nfs_clrpcops.c22
-rw-r--r--sys/fs/nfsclient/nfs_clstate.c28
3 files changed, 42 insertions, 10 deletions
diff --git a/sys/fs/nfs/nfsport.h b/sys/fs/nfs/nfsport.h
index 8c43f3f..8bd0f9e 100644
--- a/sys/fs/nfs/nfsport.h
+++ b/sys/fs/nfs/nfsport.h
@@ -899,6 +899,7 @@ int newnfs_realign(struct mbuf **, int);
*/
#define NFSSTA_HASWRITEVERF 0x00040000 /* Has write verifier */
#define NFSSTA_GOTFSINFO 0x00100000 /* Got the fsinfo */
+#define NFSSTA_OPENMODE 0x00200000 /* Must use correct open mode */
#define NFSSTA_NOLAYOUTCOMMIT 0x04000000 /* Don't do LayoutCommit */
#define NFSSTA_SESSPERSIST 0x08000000 /* Has a persistent session */
#define NFSSTA_TIMEO 0x10000000 /* Experiencing a timeout */
@@ -929,6 +930,7 @@ int newnfs_realign(struct mbuf **, int);
#define NFSHASNOLAYOUTCOMMIT(n) ((n)->nm_state & NFSSTA_NOLAYOUTCOMMIT)
#define NFSHASSESSPERSIST(n) ((n)->nm_state & NFSSTA_SESSPERSIST)
#define NFSHASPNFS(n) ((n)->nm_state & NFSSTA_PNFS)
+#define NFSHASOPENMODE(n) ((n)->nm_state & NFSSTA_OPENMODE)
#define NFSHASONEOPENOWN(n) (((n)->nm_flag & NFSMNT_ONEOPENOWN) != 0 && \
(n)->nm_minorvers > 0)
diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c
index 32c6188..38d2f73 100644
--- a/sys/fs/nfsclient/nfs_clrpcops.c
+++ b/sys/fs/nfsclient/nfs_clrpcops.c
@@ -1134,6 +1134,11 @@ nfsrpc_setattr(vnode_t vp, struct vattr *vap, NFSACL_T *aclp,
else
error = nfsrpc_setaclrpc(vp, cred, p, aclp, &stateid,
stuff);
+ if (error == NFSERR_OPENMODE && mode == NFSV4OPEN_ACCESSREAD) {
+ NFSLOCKMNT(nmp);
+ nmp->nm_state |= NFSSTA_OPENMODE;
+ NFSUNLOCKMNT(nmp);
+ }
if (error == NFSERR_STALESTATEID)
nfscl_initiate_recovery(nmp->nm_clp);
if (lckp != NULL)
@@ -1154,7 +1159,9 @@ nfsrpc_setattr(vnode_t vp, struct vattr *vap, NFSACL_T *aclp,
error == NFSERR_BADSESSION ||
(error == NFSERR_OLDSTATEID && retrycnt < 20) ||
((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
- expireret == 0 && clidrev != 0 && retrycnt < 4));
+ expireret == 0 && clidrev != 0 && retrycnt < 4) ||
+ (error == NFSERR_OPENMODE && mode == NFSV4OPEN_ACCESSREAD &&
+ retrycnt < 4));
if (error && retrycnt >= 4)
error = EIO;
return (error);
@@ -1391,6 +1398,11 @@ nfsrpc_read(vnode_t vp, struct uio *uiop, struct ucred *cred,
&lckp);
error = nfsrpc_readrpc(vp, uiop, newcred, &stateid, p, nap,
attrflagp, stuff);
+ if (error == NFSERR_OPENMODE) {
+ NFSLOCKMNT(nmp);
+ nmp->nm_state |= NFSSTA_OPENMODE;
+ NFSUNLOCKMNT(nmp);
+ }
if (error == NFSERR_STALESTATEID)
nfscl_initiate_recovery(nmp->nm_clp);
if (lckp != NULL)
@@ -1409,7 +1421,8 @@ nfsrpc_read(vnode_t vp, struct uio *uiop, struct ucred *cred,
error == NFSERR_BADSESSION ||
(error == NFSERR_OLDSTATEID && retrycnt < 20) ||
((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
- expireret == 0 && clidrev != 0 && retrycnt < 4));
+ expireret == 0 && clidrev != 0 && retrycnt < 4) ||
+ (error == NFSERR_OPENMODE && retrycnt < 4));
if (error && retrycnt >= 4)
error = EIO;
if (NFSHASNFSV4(nmp))
@@ -5594,6 +5607,11 @@ nfscl_doiods(vnode_t vp, struct uio *uiop, int *iomode, int *must_commit,
if (lastbyte > layp->nfsly_lastbyte)
layp->nfsly_lastbyte = lastbyte;
NFSUNLOCKCLSTATE();
+ } else if (error == NFSERR_OPENMODE &&
+ rwaccess == NFSV4OPEN_ACCESSREAD) {
+ NFSLOCKMNT(nmp);
+ nmp->nm_state |= NFSSTA_OPENMODE;
+ NFSUNLOCKMNT(nmp);
}
} else
error = EIO;
diff --git a/sys/fs/nfsclient/nfs_clstate.c b/sys/fs/nfsclient/nfs_clstate.c
index f12757d..0842e52 100644
--- a/sys/fs/nfsclient/nfs_clstate.c
+++ b/sys/fs/nfsclient/nfs_clstate.c
@@ -500,10 +500,11 @@ nfscl_getstateid(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t mode,
{
struct nfsclclient *clp;
struct nfsclowner *owp;
- struct nfsclopen *op = NULL;
+ struct nfsclopen *op = NULL, *top;
struct nfscllockowner *lp;
struct nfscldeleg *dp;
struct nfsnode *np;
+ struct nfsmount *nmp;
u_int8_t own[NFSV4CL_LOCKNAMELEN];
int error, done;
@@ -521,8 +522,9 @@ nfscl_getstateid(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t mode,
if (vnode_vtype(vp) != VREG)
return (EISDIR);
np = VTONFS(vp);
+ nmp = VFSTONFS(vnode_mount(vp));
NFSLOCKCLSTATE();
- clp = nfscl_findcl(VFSTONFS(vnode_mount(vp)));
+ clp = nfscl_findcl(nmp);
if (clp == NULL) {
NFSUNLOCKCLSTATE();
return (EACCES);
@@ -592,23 +594,33 @@ nfscl_getstateid(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t mode,
}
if (op == NULL) {
/* If not found, just look for any OpenOwner that will work. */
+ top = NULL;
done = 0;
owp = LIST_FIRST(&clp->nfsc_owner);
while (!done && owp != NULL) {
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
if (op->nfso_fhlen == fhlen &&
- !NFSBCMP(op->nfso_fh, nfhp, fhlen) &&
- (mode & op->nfso_mode) == mode) {
- done = 1;
- break;
+ !NFSBCMP(op->nfso_fh, nfhp, fhlen)) {
+ if (top == NULL && (op->nfso_mode &
+ NFSV4OPEN_ACCESSWRITE) != 0 &&
+ (mode & NFSV4OPEN_ACCESSREAD) != 0)
+ top = op;
+ if ((mode & op->nfso_mode) == mode) {
+ done = 1;
+ break;
+ }
}
}
if (!done)
owp = LIST_NEXT(owp, nfsow_list);
}
if (!done) {
- NFSUNLOCKCLSTATE();
- return (ENOENT);
+ NFSCL_DEBUG(2, "openmode top=%p\n", top);
+ if (top == NULL || NFSHASOPENMODE(nmp)) {
+ NFSUNLOCKCLSTATE();
+ return (ENOENT);
+ } else
+ op = top;
}
/*
* For read aheads or write behinds, use the open cred.
OpenPOWER on IntegriCloud