diff mbox

[10/11] NFSv4.1: Enable open-by-filehandle

Message ID 1363698463-3681-10-git-send-email-Trond.Myklebust@netapp.com (mailing list archive)
State New, archived
Headers show

Commit Message

Trond Myklebust March 19, 2013, 1:07 p.m. UTC
Sometimes, we actually _want_ to do open-by-filehandle, for instance
when recovering opens after a network partition, or when called
from nfs4_file_open.
Enable that functionality using a new capability NFS_CAP_ATOMIC_OPEN_V1,
and which is only enabled for NFSv4.1 servers that support it.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
---
 fs/nfs/dir.c              |  2 ++
 fs/nfs/nfs4proc.c         | 53 ++++++++++++++++++++++++++++++++++++++++-------
 include/linux/nfs_fs_sb.h |  1 +
 3 files changed, 49 insertions(+), 7 deletions(-)

Comments

J. Bruce Fields March 19, 2013, 2:09 p.m. UTC | #1
On Tue, Mar 19, 2013 at 09:07:42AM -0400, Trond Myklebust wrote:
> Sometimes, we actually _want_ to do open-by-filehandle, for instance
> when recovering opens after a network partition, or when called
> from nfs4_file_open.
> Enable that functionality using a new capability NFS_CAP_ATOMIC_OPEN_V1,
> and which is only enabled for NFSv4.1 servers that support it.

So you're assuming NFS4ERR_INVAL is how the server indicates lack of
support?

Looking back at NFS server history.... I think that's what it did before
supporting these types, but I wonder if that was really right.  Possibly
it's just a bug not to support the new claim types in a 4.1 server.

--b.

> 
> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
> ---
>  fs/nfs/dir.c              |  2 ++
>  fs/nfs/nfs4proc.c         | 53 ++++++++++++++++++++++++++++++++++++++++-------
>  include/linux/nfs_fs_sb.h |  1 +
>  3 files changed, 49 insertions(+), 7 deletions(-)
> 
> diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
> index f23f455..e093e73 100644
> --- a/fs/nfs/dir.c
> +++ b/fs/nfs/dir.c
> @@ -1486,6 +1486,8 @@ static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags)
>  		goto no_open;
>  	if (d_mountpoint(dentry))
>  		goto no_open;
> +	if (NFS_SB(dentry->d_sb)->caps & NFS_CAP_ATOMIC_OPEN_V1)
> +		goto no_open;
>  
>  	inode = dentry->d_inode;
>  	parent = dget_parent(dentry);
> diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
> index 712eb70..480b5d0 100644
> --- a/fs/nfs/nfs4proc.c
> +++ b/fs/nfs/nfs4proc.c
> @@ -767,6 +767,35 @@ struct nfs4_opendata {
>  	int cancelled;
>  };
>  
> +static bool nfs4_clear_cap_atomic_open_v1(struct nfs_server *server,
> +		int err, struct nfs4_exception *exception)
> +{
> +	if (err != -EINVAL)
> +		return false;
> +	if (!(server->caps & NFS_CAP_ATOMIC_OPEN_V1))
> +		return false;
> +	server->caps &= ~NFS_CAP_ATOMIC_OPEN_V1;
> +	exception->retry = 1;
> +	return true;
> +}
> +
> +static enum open_claim_type4
> +nfs4_map_atomic_open_claim(struct nfs_server *server,
> +		enum open_claim_type4 claim)
> +{
> +	if (server->caps & NFS_CAP_ATOMIC_OPEN_V1)
> +		return claim;
> +	switch (claim) {
> +	default:
> +		return claim;
> +	case NFS4_OPEN_CLAIM_FH:
> +		return NFS4_OPEN_CLAIM_NULL;
> +	case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
> +		return NFS4_OPEN_CLAIM_DELEGATE_CUR;
> +	case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
> +		return NFS4_OPEN_CLAIM_DELEGATE_PREV;
> +	}
> +}
>  
>  static void nfs4_init_opendata_res(struct nfs4_opendata *p)
>  {
> @@ -818,8 +847,8 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
>  	p->o_arg.server = server;
>  	p->o_arg.bitmask = server->attr_bitmask;
>  	p->o_arg.open_bitmap = &nfs4_fattr_bitmap[0];
> -	p->o_arg.claim = claim;
> -	switch (claim) {
> +	p->o_arg.claim = nfs4_map_atomic_open_claim(server, claim);
> +	switch (p->o_arg.claim) {
>  	case NFS4_OPEN_CLAIM_NULL:
>  	case NFS4_OPEN_CLAIM_DELEGATE_CUR:
>  	case NFS4_OPEN_CLAIM_DELEGATE_PREV:
> @@ -1326,6 +1355,8 @@ static int nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state
>  	int err;
>  	do {
>  		err = _nfs4_do_open_reclaim(ctx, state);
> +		if (nfs4_clear_cap_atomic_open_v1(server, err, &exception))
> +			continue;
>  		if (err != -NFS4ERR_DELAY)
>  			break;
>  		nfs4_handle_exception(server, err, &exception);
> @@ -1741,7 +1772,7 @@ static int _nfs4_open_expired(struct nfs_open_context *ctx, struct nfs4_state *s
>  	int ret;
>  
>  	opendata = nfs4_open_recoverdata_alloc(ctx, state,
> -			NFS4_OPEN_CLAIM_NULL);
> +			NFS4_OPEN_CLAIM_FH);
>  	if (IS_ERR(opendata))
>  		return PTR_ERR(opendata);
>  	ret = nfs4_open_recover(opendata, state);
> @@ -1759,6 +1790,8 @@ static int nfs4_do_open_expired(struct nfs_open_context *ctx, struct nfs4_state
>  
>  	do {
>  		err = _nfs4_open_expired(ctx, state);
> +		if (nfs4_clear_cap_atomic_open_v1(server, err, &exception))
> +			continue;
>  		switch (err) {
>  		default:
>  			goto out;
> @@ -1926,6 +1959,7 @@ static int _nfs4_do_open(struct inode *dir,
>  	struct nfs4_state     *state = NULL;
>  	struct nfs_server       *server = NFS_SERVER(dir);
>  	struct nfs4_opendata *opendata;
> +	enum open_claim_type4 claim = NFS4_OPEN_CLAIM_NULL;
>  	int status;
>  
>  	/* Protect against reboot recovery conflicts */
> @@ -1941,9 +1975,10 @@ static int _nfs4_do_open(struct inode *dir,
>  	if (dentry->d_inode != NULL)
>  		nfs4_return_incompatible_delegation(dentry->d_inode, fmode);
>  	status = -ENOMEM;
> +	if (dentry->d_inode)
> +		claim = NFS4_OPEN_CLAIM_FH;
>  	opendata = nfs4_opendata_alloc(dentry, sp, fmode, flags, sattr,
> -			NFS4_OPEN_CLAIM_NULL,
> -			GFP_KERNEL);
> +			claim, GFP_KERNEL);
>  	if (opendata == NULL)
>  		goto err_put_state_owner;
>  
> @@ -2001,6 +2036,7 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir,
>  					struct rpc_cred *cred,
>  					struct nfs4_threshold **ctx_th)
>  {
> +	struct nfs_server *server = NFS_SERVER(dir);
>  	struct nfs4_exception exception = { };
>  	struct nfs4_state *res;
>  	int status;
> @@ -2044,7 +2080,9 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir,
>  			exception.retry = 1;
>  			continue;
>  		}
> -		res = ERR_PTR(nfs4_handle_exception(NFS_SERVER(dir),
> +		if (nfs4_clear_cap_atomic_open_v1(server, status, &exception))
> +			continue;
> +		res = ERR_PTR(nfs4_handle_exception(server,
>  					status, &exception));
>  	} while (exception.retry);
>  	return res;
> @@ -6872,7 +6910,8 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
>  		| NFS_CAP_ATOMIC_OPEN
>  		| NFS_CAP_CHANGE_ATTR
>  		| NFS_CAP_POSIX_LOCK
> -		| NFS_CAP_STATEID_NFSV41,
> +		| NFS_CAP_STATEID_NFSV41
> +		| NFS_CAP_ATOMIC_OPEN_V1,
>  	.call_sync = nfs4_call_sync_sequence,
>  	.match_stateid = nfs41_match_stateid,
>  	.find_root_sec = nfs41_find_root_sec,
> diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
> index 74c9e52..d8fdfdc 100644
> --- a/include/linux/nfs_fs_sb.h
> +++ b/include/linux/nfs_fs_sb.h
> @@ -198,5 +198,6 @@ struct nfs_server {
>  #define NFS_CAP_POSIX_LOCK	(1U << 14)
>  #define NFS_CAP_UIDGID_NOMAP	(1U << 15)
>  #define NFS_CAP_STATEID_NFSV41	(1U << 16)
> +#define NFS_CAP_ATOMIC_OPEN_V1	(1U << 17)
>  
>  #endif
> -- 
> 1.8.1.4
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Trond Myklebust March 19, 2013, 2:16 p.m. UTC | #2
On Tue, 2013-03-19 at 10:09 -0400, J. Bruce Fields wrote:
> On Tue, Mar 19, 2013 at 09:07:42AM -0400, Trond Myklebust wrote:
> > Sometimes, we actually _want_ to do open-by-filehandle, for instance
> > when recovering opens after a network partition, or when called
> > from nfs4_file_open.
> > Enable that functionality using a new capability NFS_CAP_ATOMIC_OPEN_V1,
> > and which is only enabled for NFSv4.1 servers that support it.
> 
> So you're assuming NFS4ERR_INVAL is how the server indicates lack of
> support?

Looking at the list of valid errors for OPEN in section 15.2 of RFC5661,
I don't see what else fits the bill.

> Looking back at NFS server history.... I think that's what it did before
> supporting these types, but I wonder if that was really right.  Possibly
> it's just a bug not to support the new claim types in a 4.1 server.

I've assumed that it isn't. NFSv4.1 is the very first minor version, so
it's not supposed to contain any mandatory new features. Yes, I know we
broke the rules on that one in spectacular fashion with sessions, but
I'm assuming that is the only exception...
J. Bruce Fields March 19, 2013, 2:32 p.m. UTC | #3
On Tue, Mar 19, 2013 at 02:16:15PM +0000, Myklebust, Trond wrote:
> On Tue, 2013-03-19 at 10:09 -0400, J. Bruce Fields wrote:
> > On Tue, Mar 19, 2013 at 09:07:42AM -0400, Trond Myklebust wrote:
> > > Sometimes, we actually _want_ to do open-by-filehandle, for instance
> > > when recovering opens after a network partition, or when called
> > > from nfs4_file_open.
> > > Enable that functionality using a new capability NFS_CAP_ATOMIC_OPEN_V1,
> > > and which is only enabled for NFSv4.1 servers that support it.
> > 
> > So you're assuming NFS4ERR_INVAL is how the server indicates lack of
> > support?
> 
> Looking at the list of valid errors for OPEN in section 15.2 of RFC5661,
> I don't see what else fits the bill.

OK, fair enough.

> > Looking back at NFS server history.... I think that's what it did before
> > supporting these types, but I wonder if that was really right.  Possibly
> > it's just a bug not to support the new claim types in a 4.1 server.
> 
> I've assumed that it isn't. NFSv4.1 is the very first minor version, so
> it's not supposed to contain any mandatory new features. Yes, I know we
> broke the rules on that one in spectacular fashion with sessions, but
> I'm assuming that is the only exception...

It's really unclear.  There's a lot of stuff like this that aren't
clearly identified as optional or given an obvious way to negotiate.
And which sessions features are mandatory?  Even where the spec clearly
makes things mandatory for servers to implement (SSV, trunking) I wonder
whether servers are actually complying.

(Sorry for the digression, I was just trying to decide whether I can get
away with turning on 4.1 by default before implementing SP4_MACH_CRED or
GSS on the backchannel....)

--b.
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Trond Myklebust March 19, 2013, 2:41 p.m. UTC | #4
On Tue, 2013-03-19 at 10:32 -0400, J. Bruce Fields wrote:
> (Sorry for the digression, I was just trying to decide whether I can get
> away with turning on 4.1 by default before implementing SP4_MACH_CRED or
> GSS on the backchannel....)

If you don't, could you please at least offer up a module parameter that
allows me to do that? The current thing is a PITA, since it isn't
supported by any distro scripts, and it requires you to switch off the
server in order to change the version pseudofile contents.
Trond Myklebust March 19, 2013, 2:45 p.m. UTC | #5
On Tue, 2013-03-19 at 10:41 -0400, Trond Myklebust wrote:
> On Tue, 2013-03-19 at 10:32 -0400, J. Bruce Fields wrote:
> > (Sorry for the digression, I was just trying to decide whether I can get
> > away with turning on 4.1 by default before implementing SP4_MACH_CRED or
> > GSS on the backchannel....)
> 
> If you don't, could you please at least offer up a module parameter that
> allows me to do that? The current thing is a PITA, since it isn't
> supported by any distro scripts, and it requires you to switch off the
> server in order to change the version pseudofile contents.
> 
BTW: you may recall that SPKM3 and UTF-8 support was mandatory in
RFC3530, as was RPCSEC_GSS support on the back channel. That didn't stop
anybody from shipping clients and servers that didn't support them...
J. Bruce Fields March 19, 2013, 2:50 p.m. UTC | #6
On Tue, Mar 19, 2013 at 02:41:42PM +0000, Myklebust, Trond wrote:
> On Tue, 2013-03-19 at 10:32 -0400, J. Bruce Fields wrote:
> > (Sorry for the digression, I was just trying to decide whether I can get
> > away with turning on 4.1 by default before implementing SP4_MACH_CRED or
> > GSS on the backchannel....)
> 
> If you don't, could you please at least offer up a module parameter that
> allows me to do that? The current thing is a PITA, since it isn't
> supported by any distro scripts, and it requires you to switch off the
> server in order to change the version pseudofile contents.

The module parameter on its own wouldn't help with that.

We could probably fix the pseudofile interface to allow changing the
version on the fly.

My vague plan was to teach rpc.nfsd to read this (and other parameters)
from a config file, so then it'd be "vi /etc/nfsd && service nfsd
restart" or equivalent.

So is the main thing you want a more convenient interface or a way to
change it without rebooting?

--b.
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
J. Bruce Fields March 19, 2013, 3:16 p.m. UTC | #7
On Tue, Mar 19, 2013 at 02:45:00PM +0000, Myklebust, Trond wrote:
> On Tue, 2013-03-19 at 10:41 -0400, Trond Myklebust wrote:
> > On Tue, 2013-03-19 at 10:32 -0400, J. Bruce Fields wrote:
> > > (Sorry for the digression, I was just trying to decide whether I can get
> > > away with turning on 4.1 by default before implementing SP4_MACH_CRED or
> > > GSS on the backchannel....)
> > 
> > If you don't, could you please at least offer up a module parameter that
> > allows me to do that? The current thing is a PITA, since it isn't
> > supported by any distro scripts, and it requires you to switch off the
> > server in order to change the version pseudofile contents.
> > 
> BTW: you may recall that SPKM3 and UTF-8 support was mandatory in
> RFC3530,

Yes.

> as was RPCSEC_GSS support on the back channel.

I don't agree there--maybe the spec says it's mandatory, I don't
remember, but in practice 4.0 degrades gracefully without callbacks.  In
the 4.1 case the create_session's just going to fail with an unspecified
error.

> That didn't stop anybody from shipping clients and servers that didn't
> support them...

Yeah, I may just resign myself to erroring out in some more cases.

--b.
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Trond Myklebust March 19, 2013, 3:16 p.m. UTC | #8
On Tue, 2013-03-19 at 10:50 -0400, J. Bruce Fields wrote:
> On Tue, Mar 19, 2013 at 02:41:42PM +0000, Myklebust, Trond wrote:
> > On Tue, 2013-03-19 at 10:32 -0400, J. Bruce Fields wrote:
> > > (Sorry for the digression, I was just trying to decide whether I can get
> > > away with turning on 4.1 by default before implementing SP4_MACH_CRED or
> > > GSS on the backchannel....)
> > 
> > If you don't, could you please at least offer up a module parameter that
> > allows me to do that? The current thing is a PITA, since it isn't
> > supported by any distro scripts, and it requires you to switch off the
> > server in order to change the version pseudofile contents.
> 
> The module parameter on its own wouldn't help with that.
> 
> We could probably fix the pseudofile interface to allow changing the
> version on the fly.
> 
> My vague plan was to teach rpc.nfsd to read this (and other parameters)
> from a config file, so then it'd be "vi /etc/nfsd && service nfsd
> restart" or equivalent.
> 
> So is the main thing you want a more convenient interface or a way to
> change it without rebooting?

Basically, all I want is to enable 4.1 permanently on those servers that
I want to use for NFSv4.1 testing.

An --enable-nfs-version option for rpc.nfsd that acts together with
--no-nfs-version as a convenient interface for /proc/fs/nfsd/versions
would work fine too. All distro configuration scripts that I'm aware of
allow you to specify optional arguments to rpc.nfsd.
J. Bruce Fields March 19, 2013, 3:23 p.m. UTC | #9
On Tue, Mar 19, 2013 at 03:16:46PM +0000, Myklebust, Trond wrote:
> On Tue, 2013-03-19 at 10:50 -0400, J. Bruce Fields wrote:
> > On Tue, Mar 19, 2013 at 02:41:42PM +0000, Myklebust, Trond wrote:
> > > On Tue, 2013-03-19 at 10:32 -0400, J. Bruce Fields wrote:
> > > > (Sorry for the digression, I was just trying to decide whether I can get
> > > > away with turning on 4.1 by default before implementing SP4_MACH_CRED or
> > > > GSS on the backchannel....)
> > > 
> > > If you don't, could you please at least offer up a module parameter that
> > > allows me to do that? The current thing is a PITA, since it isn't
> > > supported by any distro scripts, and it requires you to switch off the
> > > server in order to change the version pseudofile contents.
> > 
> > The module parameter on its own wouldn't help with that.
> > 
> > We could probably fix the pseudofile interface to allow changing the
> > version on the fly.
> > 
> > My vague plan was to teach rpc.nfsd to read this (and other parameters)
> > from a config file, so then it'd be "vi /etc/nfsd && service nfsd
> > restart" or equivalent.
> > 
> > So is the main thing you want a more convenient interface or a way to
> > change it without rebooting?
> 
> Basically, all I want is to enable 4.1 permanently on those servers that
> I want to use for NFSv4.1 testing.
> 
> An --enable-nfs-version option for rpc.nfsd that acts together with
> --no-nfs-version as a convenient interface for /proc/fs/nfsd/versions
> would work fine too. All distro configuration scripts that I'm aware of
> allow you to specify optional arguments to rpc.nfsd.

Right, got it.  Yes, that's on my todo list.

(But, of course, patches welcomed if someone wants to beat me to it.)

--b.
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index f23f455..e093e73 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -1486,6 +1486,8 @@  static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags)
 		goto no_open;
 	if (d_mountpoint(dentry))
 		goto no_open;
+	if (NFS_SB(dentry->d_sb)->caps & NFS_CAP_ATOMIC_OPEN_V1)
+		goto no_open;
 
 	inode = dentry->d_inode;
 	parent = dget_parent(dentry);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 712eb70..480b5d0 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -767,6 +767,35 @@  struct nfs4_opendata {
 	int cancelled;
 };
 
+static bool nfs4_clear_cap_atomic_open_v1(struct nfs_server *server,
+		int err, struct nfs4_exception *exception)
+{
+	if (err != -EINVAL)
+		return false;
+	if (!(server->caps & NFS_CAP_ATOMIC_OPEN_V1))
+		return false;
+	server->caps &= ~NFS_CAP_ATOMIC_OPEN_V1;
+	exception->retry = 1;
+	return true;
+}
+
+static enum open_claim_type4
+nfs4_map_atomic_open_claim(struct nfs_server *server,
+		enum open_claim_type4 claim)
+{
+	if (server->caps & NFS_CAP_ATOMIC_OPEN_V1)
+		return claim;
+	switch (claim) {
+	default:
+		return claim;
+	case NFS4_OPEN_CLAIM_FH:
+		return NFS4_OPEN_CLAIM_NULL;
+	case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
+		return NFS4_OPEN_CLAIM_DELEGATE_CUR;
+	case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
+		return NFS4_OPEN_CLAIM_DELEGATE_PREV;
+	}
+}
 
 static void nfs4_init_opendata_res(struct nfs4_opendata *p)
 {
@@ -818,8 +847,8 @@  static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
 	p->o_arg.server = server;
 	p->o_arg.bitmask = server->attr_bitmask;
 	p->o_arg.open_bitmap = &nfs4_fattr_bitmap[0];
-	p->o_arg.claim = claim;
-	switch (claim) {
+	p->o_arg.claim = nfs4_map_atomic_open_claim(server, claim);
+	switch (p->o_arg.claim) {
 	case NFS4_OPEN_CLAIM_NULL:
 	case NFS4_OPEN_CLAIM_DELEGATE_CUR:
 	case NFS4_OPEN_CLAIM_DELEGATE_PREV:
@@ -1326,6 +1355,8 @@  static int nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state
 	int err;
 	do {
 		err = _nfs4_do_open_reclaim(ctx, state);
+		if (nfs4_clear_cap_atomic_open_v1(server, err, &exception))
+			continue;
 		if (err != -NFS4ERR_DELAY)
 			break;
 		nfs4_handle_exception(server, err, &exception);
@@ -1741,7 +1772,7 @@  static int _nfs4_open_expired(struct nfs_open_context *ctx, struct nfs4_state *s
 	int ret;
 
 	opendata = nfs4_open_recoverdata_alloc(ctx, state,
-			NFS4_OPEN_CLAIM_NULL);
+			NFS4_OPEN_CLAIM_FH);
 	if (IS_ERR(opendata))
 		return PTR_ERR(opendata);
 	ret = nfs4_open_recover(opendata, state);
@@ -1759,6 +1790,8 @@  static int nfs4_do_open_expired(struct nfs_open_context *ctx, struct nfs4_state
 
 	do {
 		err = _nfs4_open_expired(ctx, state);
+		if (nfs4_clear_cap_atomic_open_v1(server, err, &exception))
+			continue;
 		switch (err) {
 		default:
 			goto out;
@@ -1926,6 +1959,7 @@  static int _nfs4_do_open(struct inode *dir,
 	struct nfs4_state     *state = NULL;
 	struct nfs_server       *server = NFS_SERVER(dir);
 	struct nfs4_opendata *opendata;
+	enum open_claim_type4 claim = NFS4_OPEN_CLAIM_NULL;
 	int status;
 
 	/* Protect against reboot recovery conflicts */
@@ -1941,9 +1975,10 @@  static int _nfs4_do_open(struct inode *dir,
 	if (dentry->d_inode != NULL)
 		nfs4_return_incompatible_delegation(dentry->d_inode, fmode);
 	status = -ENOMEM;
+	if (dentry->d_inode)
+		claim = NFS4_OPEN_CLAIM_FH;
 	opendata = nfs4_opendata_alloc(dentry, sp, fmode, flags, sattr,
-			NFS4_OPEN_CLAIM_NULL,
-			GFP_KERNEL);
+			claim, GFP_KERNEL);
 	if (opendata == NULL)
 		goto err_put_state_owner;
 
@@ -2001,6 +2036,7 @@  static struct nfs4_state *nfs4_do_open(struct inode *dir,
 					struct rpc_cred *cred,
 					struct nfs4_threshold **ctx_th)
 {
+	struct nfs_server *server = NFS_SERVER(dir);
 	struct nfs4_exception exception = { };
 	struct nfs4_state *res;
 	int status;
@@ -2044,7 +2080,9 @@  static struct nfs4_state *nfs4_do_open(struct inode *dir,
 			exception.retry = 1;
 			continue;
 		}
-		res = ERR_PTR(nfs4_handle_exception(NFS_SERVER(dir),
+		if (nfs4_clear_cap_atomic_open_v1(server, status, &exception))
+			continue;
+		res = ERR_PTR(nfs4_handle_exception(server,
 					status, &exception));
 	} while (exception.retry);
 	return res;
@@ -6872,7 +6910,8 @@  static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
 		| NFS_CAP_ATOMIC_OPEN
 		| NFS_CAP_CHANGE_ATTR
 		| NFS_CAP_POSIX_LOCK
-		| NFS_CAP_STATEID_NFSV41,
+		| NFS_CAP_STATEID_NFSV41
+		| NFS_CAP_ATOMIC_OPEN_V1,
 	.call_sync = nfs4_call_sync_sequence,
 	.match_stateid = nfs41_match_stateid,
 	.find_root_sec = nfs41_find_root_sec,
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index 74c9e52..d8fdfdc 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -198,5 +198,6 @@  struct nfs_server {
 #define NFS_CAP_POSIX_LOCK	(1U << 14)
 #define NFS_CAP_UIDGID_NOMAP	(1U << 15)
 #define NFS_CAP_STATEID_NFSV41	(1U << 16)
+#define NFS_CAP_ATOMIC_OPEN_V1	(1U << 17)
 
 #endif