summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrmacklem <rmacklem@FreeBSD.org>2011-07-04 23:32:09 +0000
committerrmacklem <rmacklem@FreeBSD.org>2011-07-04 23:32:09 +0000
commita1a4430906627e22d8af8232fb72573739cfd545 (patch)
tree38d51aba9a7ea76019a3107bf385a703ba16d2ff
parentfca16415f470564eb52fd10132f187322e74b160 (diff)
downloadFreeBSD-src-a1a4430906627e22d8af8232fb72573739cfd545.zip
FreeBSD-src-a1a4430906627e22d8af8232fb72573739cfd545.tar.gz
The algorithm used by nfscl_getopen() could have resulted in
multiple instances of the same lock_owner when a process both inherited an open file descriptor plus opened the same file itself. Since some NFSv4 servers cannot handle multiple instances of the same lock_owner string, this patch changes the algorithm used by nfscl_getopen() in the new NFSv4 client to keep that from happening. The new algorithm is simpler, since there is no longer any need to ascend the process's parentage tree because all NFSv4 Closes for a file are done at VOP_INACTIVE()/VOP_RECLAIM(), making the Opens indistinct w.r.t. use with Lock Ops. This problem was discovered at the recent NFSv4 interoperability Bakeathon. MFC after: 2 weeks
-rw-r--r--sys/fs/nfsclient/nfs_clstate.c175
1 files changed, 94 insertions, 81 deletions
diff --git a/sys/fs/nfsclient/nfs_clstate.c b/sys/fs/nfsclient/nfs_clstate.c
index 0b940bd..b136974 100644
--- a/sys/fs/nfsclient/nfs_clstate.c
+++ b/sys/fs/nfsclient/nfs_clstate.c
@@ -95,7 +95,7 @@ int nfscl_deleghighwater = NFSCLDELEGHIGHWATER;
static int nfscl_delegcnt = 0;
static int nfscl_getopen(struct nfsclownerhead *, u_int8_t *, int, u_int8_t *,
- NFSPROC_T *, u_int32_t, struct nfsclowner **, struct nfsclopen **);
+ u_int8_t *, u_int32_t, struct nfscllockowner **, struct nfsclopen **);
static void nfscl_clrelease(struct nfsclclient *);
static void nfscl_cleanclient(struct nfsclclient *);
static void nfscl_expireclient(struct nfsclclient *, struct nfsmount *,
@@ -521,25 +521,20 @@ nfscl_getstateid(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t mode,
* for a matching OpenOwner and use that.
*/
nfscl_filllockowner(p->td_proc, own, F_POSIX);
- error = nfscl_getopen(&clp->nfsc_owner, nfhp, fhlen, NULL, p,
- mode, NULL, &op);
- if (error == 0) {
- /* now look for a lockowner */
- LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
- if (!NFSBCMP(lp->nfsl_owner, own,
- NFSV4CL_LOCKNAMELEN)) {
- stateidp->seqid =
- lp->nfsl_stateid.seqid;
- stateidp->other[0] =
- lp->nfsl_stateid.other[0];
- stateidp->other[1] =
- lp->nfsl_stateid.other[1];
- stateidp->other[2] =
- lp->nfsl_stateid.other[2];
- NFSUNLOCKCLSTATE();
- return (0);
- }
- }
+ lp = NULL;
+ error = nfscl_getopen(&clp->nfsc_owner, nfhp, fhlen, own, own,
+ mode, &lp, &op);
+ if (error == 0 && lp != NULL) {
+ stateidp->seqid =
+ lp->nfsl_stateid.seqid;
+ stateidp->other[0] =
+ lp->nfsl_stateid.other[0];
+ stateidp->other[1] =
+ lp->nfsl_stateid.other[1];
+ stateidp->other[2] =
+ lp->nfsl_stateid.other[2];
+ NFSUNLOCKCLSTATE();
+ return (0);
}
}
if (op == NULL) {
@@ -578,55 +573,74 @@ nfscl_getstateid(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t mode,
}
/*
- * Get an existing open. Search up the parentage tree for a match and
- * return with the first one found.
+ * Search for a matching file, mode and, optionally, lockowner.
*/
static int
nfscl_getopen(struct nfsclownerhead *ohp, u_int8_t *nfhp, int fhlen,
- u_int8_t *rown, NFSPROC_T *p, u_int32_t mode, struct nfsclowner **owpp,
- struct nfsclopen **opp)
+ u_int8_t *openown, u_int8_t *lockown, u_int32_t mode,
+ struct nfscllockowner **lpp, struct nfsclopen **opp)
{
- struct nfsclowner *owp = NULL;
- struct nfsclopen *op;
- NFSPROC_T *nproc;
- u_int8_t own[NFSV4CL_LOCKNAMELEN], *ownp;
+ struct nfsclowner *owp;
+ struct nfsclopen *op, *rop, *rop2;
+ struct nfscllockowner *lp;
+ int keep_looping;
- nproc = p;
- op = NULL;
- while (op == NULL && (nproc != NULL || rown != NULL)) {
- if (nproc != NULL) {
- nfscl_filllockowner(nproc->td_proc, own, F_POSIX);
- ownp = own;
- } else {
- ownp = rown;
- }
- /* Search the client list */
- LIST_FOREACH(owp, ohp, nfsow_list) {
- if (!NFSBCMP(owp->nfsow_owner, ownp,
- NFSV4CL_LOCKNAMELEN))
- break;
- }
- if (owp != NULL) {
- /* and look for the correct open */
- LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
- if (op->nfso_fhlen == fhlen &&
- !NFSBCMP(op->nfso_fh, nfhp, fhlen)
- && (op->nfso_mode & mode) == mode) {
- break;
+ if (lpp != NULL)
+ *lpp = NULL;
+ /*
+ * rop will be set to the open to be returned. There are three
+ * variants of this, all for an open of the correct file:
+ * 1 - A match of lockown.
+ * 2 - A match of the openown, when no lockown match exists.
+ * 3 - A match for any open, if no openown or lockown match exists.
+ * Looking for #2 over #3 probably isn't necessary, but since
+ * RFC3530 is vague w.r.t. the relationship between openowners and
+ * lockowners, I think this is the safer way to go.
+ */
+ rop = NULL;
+ rop2 = NULL;
+ keep_looping = 1;
+ /* Search the client list */
+ owp = LIST_FIRST(ohp);
+ while (owp != NULL && keep_looping != 0) {
+ /* and look for the correct open */
+ op = LIST_FIRST(&owp->nfsow_open);
+ while (op != NULL && keep_looping != 0) {
+ if (op->nfso_fhlen == fhlen &&
+ !NFSBCMP(op->nfso_fh, nfhp, fhlen)
+ && (op->nfso_mode & mode) == mode) {
+ if (lpp != NULL) {
+ /* Now look for a matching lockowner. */
+ LIST_FOREACH(lp, &op->nfso_lock,
+ nfsl_list) {
+ if (!NFSBCMP(lp->nfsl_owner,
+ lockown,
+ NFSV4CL_LOCKNAMELEN)) {
+ *lpp = lp;
+ rop = op;
+ keep_looping = 0;
+ break;
+ }
+ }
}
+ if (rop == NULL && !NFSBCMP(owp->nfsow_owner,
+ openown, NFSV4CL_LOCKNAMELEN)) {
+ rop = op;
+ if (lpp == NULL)
+ keep_looping = 0;
+ }
+ if (rop2 == NULL)
+ rop2 = op;
}
+ op = LIST_NEXT(op, nfso_list);
}
- if (rown != NULL)
- break;
- if (op == NULL)
- nproc = nfscl_getparent(nproc);
+ owp = LIST_NEXT(owp, nfsow_list);
}
- if (op == NULL) {
+ if (rop == NULL)
+ rop = rop2;
+ if (rop == NULL)
return (EBADF);
- }
- if (owpp)
- *owpp = owp;
- *opp = op;
+ *opp = rop;
return (0);
}
@@ -891,16 +905,16 @@ nfscl_getbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
struct nfscldeleg *dp = NULL, *ldp = NULL;
struct nfscllockownerhead *lhp = NULL;
struct nfsnode *np;
- u_int8_t own[NFSV4CL_LOCKNAMELEN], *ownp;
+ u_int8_t own[NFSV4CL_LOCKNAMELEN], *ownp, openown[NFSV4CL_LOCKNAMELEN];
+ u_int8_t *openownp;
int error = 0, ret, donelocally = 0;
u_int32_t mode;
- if (type == F_WRLCK)
- mode = NFSV4OPEN_ACCESSWRITE;
- else
- mode = NFSV4OPEN_ACCESSREAD;
+ /* For Lock Ops, the open mode doesn't matter, so use 0 to match any. */
+ mode = 0;
np = VTONFS(vp);
*lpp = NULL;
+ lp = NULL;
*newonep = 0;
*donelocallyp = 0;
@@ -940,9 +954,12 @@ nfscl_getbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
op = NULL;
if (recovery) {
ownp = rownp;
+ openownp = ropenownp;
} else {
nfscl_filllockowner(id, own, flags);
ownp = own;
+ nfscl_filllockowner(p->td_proc, openown, F_POSIX);
+ openownp = openown;
}
if (!recovery) {
NFSLOCKCLSTATE();
@@ -961,13 +978,13 @@ nfscl_getbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
dp = NULL;
}
if (dp != NULL) {
- /* Now, find the associated open to get the correct openowner */
+ /* Now, find an open and maybe a lockowner. */
ret = nfscl_getopen(&dp->nfsdl_owner, np->n_fhp->nfh_fh,
- np->n_fhp->nfh_len, NULL, p, mode, NULL, &op);
+ np->n_fhp->nfh_len, openownp, ownp, mode, NULL, &op);
if (ret)
ret = nfscl_getopen(&clp->nfsc_owner,
- np->n_fhp->nfh_fh, np->n_fhp->nfh_len, NULL, p,
- mode, NULL, &op);
+ np->n_fhp->nfh_fh, np->n_fhp->nfh_len, openownp,
+ ownp, mode, NULL, &op);
if (!ret) {
lhp = &dp->nfsdl_lock;
TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
@@ -980,16 +997,11 @@ nfscl_getbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
}
if (!donelocally) {
/*
- * Get the related Open.
+ * Get the related Open and maybe lockowner.
*/
- if (recovery)
- error = nfscl_getopen(&clp->nfsc_owner,
- np->n_fhp->nfh_fh, np->n_fhp->nfh_len, ropenownp,
- NULL, mode, NULL, &op);
- else
- error = nfscl_getopen(&clp->nfsc_owner,
- np->n_fhp->nfh_fh, np->n_fhp->nfh_len, NULL, p,
- mode, NULL, &op);
+ error = nfscl_getopen(&clp->nfsc_owner,
+ np->n_fhp->nfh_fh, np->n_fhp->nfh_len, openownp,
+ ownp, mode, &lp, &op);
if (!error)
lhp = &op->nfso_lock;
}
@@ -1010,10 +1022,11 @@ nfscl_getbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
/*
* Ok, see if a lockowner exists and create one, as required.
*/
- LIST_FOREACH(lp, lhp, nfsl_list) {
- if (!NFSBCMP(lp->nfsl_owner, ownp, NFSV4CL_LOCKNAMELEN))
- break;
- }
+ if (lp == NULL)
+ LIST_FOREACH(lp, lhp, nfsl_list) {
+ if (!NFSBCMP(lp->nfsl_owner, ownp, NFSV4CL_LOCKNAMELEN))
+ break;
+ }
if (lp == NULL) {
NFSBCOPY(ownp, nlp->nfsl_owner, NFSV4CL_LOCKNAMELEN);
if (recovery)
OpenPOWER on IntegriCloud