diff mbox

[resend] NFS: Don't disconnect open-owner on NFS4ERR_BAD_SEQID

Message ID 87zijsu4ko.fsf@notabene.neil.brown.name (mailing list archive)
State New, archived
Headers show

Commit Message

NeilBrown Dec. 19, 2016, 12:48 a.m. UTC
When an NFS4ERR_BAD_SEQID is received the open-owner is removed from
the ->state_owners rbtree so that it will no longer be used.

If any stateids attached to this open-owner are still in use, and if a
request using one gets an NFS4ERR_BAD_STATEID reply, this can for bad.

The state is marked as needing recovery and the nfs4_state_manager()
is scheduled to clean up.  nfs4_state_manager() finds states to be
recovered by walking the state_owners rbtree.  As the open-owner is
not in the rbtree, the bad state is not found so nfs4_state_manager()
completes having done nothing.  The request is then retried, with a
predicatable result (indefinite retries).

If the stateid is for a delegation, this open_owner will be used
to open files when the delegation is returned.  For that to work,
a new open-owner needs to be presented to the server.

This patch changes NFS4ERR_BAD_SEQID handling to leave the open-owner
in the rbtree but updates the 'create_time' so it looks like a new
open-owner.  With this the indefinite retries no longer happen.

Signed-off-by: NeilBrown <neilb@suse.com>
---

Hi Trond,
 It appears this one got lost too.
 I've added a comment as I thought an explanation was needed, and
 renamed the function from "drop" to "reset".

Thanks,
NeilBrown

 fs/nfs/nfs4state.c | 29 +++++++++++++----------------
 1 file changed, 13 insertions(+), 16 deletions(-)

Comments

Olga Kornievskaia March 20, 2017, 9 p.m. UTC | #1
Since open_owner is no longer removed then the resources are not
freed. They will not be freed until unmount  (or reboot recovery).

There are (buggy) servers out there that might be forcing the client
to keep creating new open_owners. So if they are no longer released,
can't the client get into trouble here?

On Sun, Dec 18, 2016 at 7:48 PM, NeilBrown <neilb@suse.com> wrote:
>
> When an NFS4ERR_BAD_SEQID is received the open-owner is removed from
> the ->state_owners rbtree so that it will no longer be used.
>
> If any stateids attached to this open-owner are still in use, and if a
> request using one gets an NFS4ERR_BAD_STATEID reply, this can for bad.
>
> The state is marked as needing recovery and the nfs4_state_manager()
> is scheduled to clean up.  nfs4_state_manager() finds states to be
> recovered by walking the state_owners rbtree.  As the open-owner is
> not in the rbtree, the bad state is not found so nfs4_state_manager()
> completes having done nothing.  The request is then retried, with a
> predicatable result (indefinite retries).
>
> If the stateid is for a delegation, this open_owner will be used
> to open files when the delegation is returned.  For that to work,
> a new open-owner needs to be presented to the server.
>
> This patch changes NFS4ERR_BAD_SEQID handling to leave the open-owner
> in the rbtree but updates the 'create_time' so it looks like a new
> open-owner.  With this the indefinite retries no longer happen.
>
> Signed-off-by: NeilBrown <neilb@suse.com>
> ---
>
> Hi Trond,
>  It appears this one got lost too.
>  I've added a comment as I thought an explanation was needed, and
>  renamed the function from "drop" to "reset".
>
> Thanks,
> NeilBrown
>
>  fs/nfs/nfs4state.c | 29 +++++++++++++----------------
>  1 file changed, 13 insertions(+), 16 deletions(-)
>
> diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
> index cf869802ff23..1d152f4470cd 100644
> --- a/fs/nfs/nfs4state.c
> +++ b/fs/nfs/nfs4state.c
> @@ -494,21 +494,18 @@ nfs4_alloc_state_owner(struct nfs_server *server,
>  }
>
>  static void
> -nfs4_drop_state_owner(struct nfs4_state_owner *sp)
> -{
> -       struct rb_node *rb_node = &sp->so_server_node;
> -
> -       if (!RB_EMPTY_NODE(rb_node)) {
> -               struct nfs_server *server = sp->so_server;
> -               struct nfs_client *clp = server->nfs_client;
> -
> -               spin_lock(&clp->cl_lock);
> -               if (!RB_EMPTY_NODE(rb_node)) {
> -                       rb_erase(rb_node, &server->state_owners);
> -                       RB_CLEAR_NODE(rb_node);
> -               }
> -               spin_unlock(&clp->cl_lock);
> -       }
> +nfs4_reset_state_owner(struct nfs4_state_owner *sp)
> +{
> +       /* This state_owner is no longer usable, but must
> +        * remain in place so that state recovery can find it
> +        * and the opens associated with it.
> +        * It may also be used for new 'open' request to
> +        * return a delegation to the server.
> +        * So update the 'create_time' so that it looks like
> +        * a new state_owner.  This will cause the server to
> +        * request an OPEN_CONFIRM to start a new sequence.
> +        */
> +       sp->so_seqid.create_time = ktime_get();
>  }
>
>  static void nfs4_free_state_owner(struct nfs4_state_owner *sp)
> @@ -1113,7 +1110,7 @@ void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid)
>
>         sp = container_of(seqid->sequence, struct nfs4_state_owner, so_seqid);
>         if (status == -NFS4ERR_BAD_SEQID)
> -               nfs4_drop_state_owner(sp);
> +               nfs4_reset_state_owner(sp);
>         if (!nfs4_has_session(sp->so_server->nfs_client))
>                 nfs_increment_seqid(status, seqid);
>  }
> --
> 2.11.0
>
--
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
NeilBrown March 20, 2017, 11:26 p.m. UTC | #2
On Mon, Mar 20 2017, Olga Kornievskaia wrote:

> Since open_owner is no longer removed then the resources are not
> freed. They will not be freed until unmount  (or reboot recovery).

I don't follow your reasoning.
When the last uses for a state owner is dropped by
nfs4_put_state_owner() (e.g. when the last open file using it is
closed), nfs4_put_state_owner() will add the state to
server->state_owners_lru.

nfs4_gc_state_owners() is called periodically (every time any file is
opened I think) and it will call nfs4_remove_state_owner_locked()
on any state which is sufficiently old enough (hasn't been used in
'lease-time'), and will then call nfs4_free_state_owner().
These two will release all resources.

Does that explanation fit with your understanding?

Thanks,
NeilBrown


>
> There are (buggy) servers out there that might be forcing the client
> to keep creating new open_owners. So if they are no longer released,
> can't the client get into trouble here?
>
> On Sun, Dec 18, 2016 at 7:48 PM, NeilBrown <neilb@suse.com> wrote:
>>
>> When an NFS4ERR_BAD_SEQID is received the open-owner is removed from
>> the ->state_owners rbtree so that it will no longer be used.
>>
>> If any stateids attached to this open-owner are still in use, and if a
>> request using one gets an NFS4ERR_BAD_STATEID reply, this can for bad.
>>
>> The state is marked as needing recovery and the nfs4_state_manager()
>> is scheduled to clean up.  nfs4_state_manager() finds states to be
>> recovered by walking the state_owners rbtree.  As the open-owner is
>> not in the rbtree, the bad state is not found so nfs4_state_manager()
>> completes having done nothing.  The request is then retried, with a
>> predicatable result (indefinite retries).
>>
>> If the stateid is for a delegation, this open_owner will be used
>> to open files when the delegation is returned.  For that to work,
>> a new open-owner needs to be presented to the server.
>>
>> This patch changes NFS4ERR_BAD_SEQID handling to leave the open-owner
>> in the rbtree but updates the 'create_time' so it looks like a new
>> open-owner.  With this the indefinite retries no longer happen.
>>
>> Signed-off-by: NeilBrown <neilb@suse.com>
>> ---
>>
>> Hi Trond,
>>  It appears this one got lost too.
>>  I've added a comment as I thought an explanation was needed, and
>>  renamed the function from "drop" to "reset".
>>
>> Thanks,
>> NeilBrown
>>
>>  fs/nfs/nfs4state.c | 29 +++++++++++++----------------
>>  1 file changed, 13 insertions(+), 16 deletions(-)
>>
>> diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
>> index cf869802ff23..1d152f4470cd 100644
>> --- a/fs/nfs/nfs4state.c
>> +++ b/fs/nfs/nfs4state.c
>> @@ -494,21 +494,18 @@ nfs4_alloc_state_owner(struct nfs_server *server,
>>  }
>>
>>  static void
>> -nfs4_drop_state_owner(struct nfs4_state_owner *sp)
>> -{
>> -       struct rb_node *rb_node = &sp->so_server_node;
>> -
>> -       if (!RB_EMPTY_NODE(rb_node)) {
>> -               struct nfs_server *server = sp->so_server;
>> -               struct nfs_client *clp = server->nfs_client;
>> -
>> -               spin_lock(&clp->cl_lock);
>> -               if (!RB_EMPTY_NODE(rb_node)) {
>> -                       rb_erase(rb_node, &server->state_owners);
>> -                       RB_CLEAR_NODE(rb_node);
>> -               }
>> -               spin_unlock(&clp->cl_lock);
>> -       }
>> +nfs4_reset_state_owner(struct nfs4_state_owner *sp)
>> +{
>> +       /* This state_owner is no longer usable, but must
>> +        * remain in place so that state recovery can find it
>> +        * and the opens associated with it.
>> +        * It may also be used for new 'open' request to
>> +        * return a delegation to the server.
>> +        * So update the 'create_time' so that it looks like
>> +        * a new state_owner.  This will cause the server to
>> +        * request an OPEN_CONFIRM to start a new sequence.
>> +        */
>> +       sp->so_seqid.create_time = ktime_get();
>>  }
>>
>>  static void nfs4_free_state_owner(struct nfs4_state_owner *sp)
>> @@ -1113,7 +1110,7 @@ void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid)
>>
>>         sp = container_of(seqid->sequence, struct nfs4_state_owner, so_seqid);
>>         if (status == -NFS4ERR_BAD_SEQID)
>> -               nfs4_drop_state_owner(sp);
>> +               nfs4_reset_state_owner(sp);
>>         if (!nfs4_has_session(sp->so_server->nfs_client))
>>                 nfs_increment_seqid(status, seqid);
>>  }
>> --
>> 2.11.0
>>
Olga Kornievskaia March 21, 2017, 4:43 p.m. UTC | #3
On Mon, Mar 20, 2017 at 7:26 PM, NeilBrown <neilb@suse.com> wrote:
> On Mon, Mar 20 2017, Olga Kornievskaia wrote:
>
>> Since open_owner is no longer removed then the resources are not
>> freed. They will not be freed until unmount  (or reboot recovery).
>
> I don't follow your reasoning.
> When the last uses for a state owner is dropped by
> nfs4_put_state_owner() (e.g. when the last open file using it is
> closed), nfs4_put_state_owner() will add the state to
> server->state_owners_lru.
>
> nfs4_gc_state_owners() is called periodically (every time any file is
> opened I think) and it will call nfs4_remove_state_owner_locked()
> on any state which is sufficiently old enough (hasn't been used in
> 'lease-time'), and will then call nfs4_free_state_owner().
> These two will release all resources.
>
> Does that explanation fit with your understanding?

Thanks for the explanation. I was thinking about the rb-tree nodes.
The new open finds the same rb-tree node. I incorrectly thought that
it'd be generating a new node each time for the new open owner.

Thanks.

>
> Thanks,
> NeilBrown
>
>
>>
>> There are (buggy) servers out there that might be forcing the client
>> to keep creating new open_owners. So if they are no longer released,
>> can't the client get into trouble here?
>>
>> On Sun, Dec 18, 2016 at 7:48 PM, NeilBrown <neilb@suse.com> wrote:
>>>
>>> When an NFS4ERR_BAD_SEQID is received the open-owner is removed from
>>> the ->state_owners rbtree so that it will no longer be used.
>>>
>>> If any stateids attached to this open-owner are still in use, and if a
>>> request using one gets an NFS4ERR_BAD_STATEID reply, this can for bad.
>>>
>>> The state is marked as needing recovery and the nfs4_state_manager()
>>> is scheduled to clean up.  nfs4_state_manager() finds states to be
>>> recovered by walking the state_owners rbtree.  As the open-owner is
>>> not in the rbtree, the bad state is not found so nfs4_state_manager()
>>> completes having done nothing.  The request is then retried, with a
>>> predicatable result (indefinite retries).
>>>
>>> If the stateid is for a delegation, this open_owner will be used
>>> to open files when the delegation is returned.  For that to work,
>>> a new open-owner needs to be presented to the server.
>>>
>>> This patch changes NFS4ERR_BAD_SEQID handling to leave the open-owner
>>> in the rbtree but updates the 'create_time' so it looks like a new
>>> open-owner.  With this the indefinite retries no longer happen.
>>>
>>> Signed-off-by: NeilBrown <neilb@suse.com>
>>> ---
>>>
>>> Hi Trond,
>>>  It appears this one got lost too.
>>>  I've added a comment as I thought an explanation was needed, and
>>>  renamed the function from "drop" to "reset".
>>>
>>> Thanks,
>>> NeilBrown
>>>
>>>  fs/nfs/nfs4state.c | 29 +++++++++++++----------------
>>>  1 file changed, 13 insertions(+), 16 deletions(-)
>>>
>>> diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
>>> index cf869802ff23..1d152f4470cd 100644
>>> --- a/fs/nfs/nfs4state.c
>>> +++ b/fs/nfs/nfs4state.c
>>> @@ -494,21 +494,18 @@ nfs4_alloc_state_owner(struct nfs_server *server,
>>>  }
>>>
>>>  static void
>>> -nfs4_drop_state_owner(struct nfs4_state_owner *sp)
>>> -{
>>> -       struct rb_node *rb_node = &sp->so_server_node;
>>> -
>>> -       if (!RB_EMPTY_NODE(rb_node)) {
>>> -               struct nfs_server *server = sp->so_server;
>>> -               struct nfs_client *clp = server->nfs_client;
>>> -
>>> -               spin_lock(&clp->cl_lock);
>>> -               if (!RB_EMPTY_NODE(rb_node)) {
>>> -                       rb_erase(rb_node, &server->state_owners);
>>> -                       RB_CLEAR_NODE(rb_node);
>>> -               }
>>> -               spin_unlock(&clp->cl_lock);
>>> -       }
>>> +nfs4_reset_state_owner(struct nfs4_state_owner *sp)
>>> +{
>>> +       /* This state_owner is no longer usable, but must
>>> +        * remain in place so that state recovery can find it
>>> +        * and the opens associated with it.
>>> +        * It may also be used for new 'open' request to
>>> +        * return a delegation to the server.
>>> +        * So update the 'create_time' so that it looks like
>>> +        * a new state_owner.  This will cause the server to
>>> +        * request an OPEN_CONFIRM to start a new sequence.
>>> +        */
>>> +       sp->so_seqid.create_time = ktime_get();
>>>  }
>>>
>>>  static void nfs4_free_state_owner(struct nfs4_state_owner *sp)
>>> @@ -1113,7 +1110,7 @@ void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid)
>>>
>>>         sp = container_of(seqid->sequence, struct nfs4_state_owner, so_seqid);
>>>         if (status == -NFS4ERR_BAD_SEQID)
>>> -               nfs4_drop_state_owner(sp);
>>> +               nfs4_reset_state_owner(sp);
>>>         if (!nfs4_has_session(sp->so_server->nfs_client))
>>>                 nfs_increment_seqid(status, seqid);
>>>  }
>>> --
>>> 2.11.0
>>>
--
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
Olga Kornievskaia March 8, 2018, 6:58 p.m. UTC | #4
On Tue, Mar 21, 2017 at 12:43 PM, Olga Kornievskaia <aglo@umich.edu> wrote:
> On Mon, Mar 20, 2017 at 7:26 PM, NeilBrown <neilb@suse.com> wrote:
>> On Mon, Mar 20 2017, Olga Kornievskaia wrote:
>>
>>> Since open_owner is no longer removed then the resources are not
>>> freed. They will not be freed until unmount  (or reboot recovery).
>>
>> I don't follow your reasoning.
>> When the last uses for a state owner is dropped by
>> nfs4_put_state_owner() (e.g. when the last open file using it is
>> closed), nfs4_put_state_owner() will add the state to
>> server->state_owners_lru.
>>
>> nfs4_gc_state_owners() is called periodically (every time any file is
>> opened I think) and it will call nfs4_remove_state_owner_locked()
>> on any state which is sufficiently old enough (hasn't been used in
>> 'lease-time'), and will then call nfs4_free_state_owner().
>> These two will release all resources.
>>
>> Does that explanation fit with your understanding?
>
> Thanks for the explanation. I was thinking about the rb-tree nodes.
> The new open finds the same rb-tree node. I incorrectly thought that
> it'd be generating a new node each time for the new open owner.

I have another question about this patch. With this patch, when the
new open owner is sent, it is sent with a seqid of what the old open
owner had. Is this intentional or accidental?

>
> Thanks.
>
>>
>> Thanks,
>> NeilBrown
>>
>>
>>>
>>> There are (buggy) servers out there that might be forcing the client
>>> to keep creating new open_owners. So if they are no longer released,
>>> can't the client get into trouble here?
>>>
>>> On Sun, Dec 18, 2016 at 7:48 PM, NeilBrown <neilb@suse.com> wrote:
>>>>
>>>> When an NFS4ERR_BAD_SEQID is received the open-owner is removed from
>>>> the ->state_owners rbtree so that it will no longer be used.
>>>>
>>>> If any stateids attached to this open-owner are still in use, and if a
>>>> request using one gets an NFS4ERR_BAD_STATEID reply, this can for bad.
>>>>
>>>> The state is marked as needing recovery and the nfs4_state_manager()
>>>> is scheduled to clean up.  nfs4_state_manager() finds states to be
>>>> recovered by walking the state_owners rbtree.  As the open-owner is
>>>> not in the rbtree, the bad state is not found so nfs4_state_manager()
>>>> completes having done nothing.  The request is then retried, with a
>>>> predicatable result (indefinite retries).
>>>>
>>>> If the stateid is for a delegation, this open_owner will be used
>>>> to open files when the delegation is returned.  For that to work,
>>>> a new open-owner needs to be presented to the server.
>>>>
>>>> This patch changes NFS4ERR_BAD_SEQID handling to leave the open-owner
>>>> in the rbtree but updates the 'create_time' so it looks like a new
>>>> open-owner.  With this the indefinite retries no longer happen.
>>>>
>>>> Signed-off-by: NeilBrown <neilb@suse.com>
>>>> ---
>>>>
>>>> Hi Trond,
>>>>  It appears this one got lost too.
>>>>  I've added a comment as I thought an explanation was needed, and
>>>>  renamed the function from "drop" to "reset".
>>>>
>>>> Thanks,
>>>> NeilBrown
>>>>
>>>>  fs/nfs/nfs4state.c | 29 +++++++++++++----------------
>>>>  1 file changed, 13 insertions(+), 16 deletions(-)
>>>>
>>>> diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
>>>> index cf869802ff23..1d152f4470cd 100644
>>>> --- a/fs/nfs/nfs4state.c
>>>> +++ b/fs/nfs/nfs4state.c
>>>> @@ -494,21 +494,18 @@ nfs4_alloc_state_owner(struct nfs_server *server,
>>>>  }
>>>>
>>>>  static void
>>>> -nfs4_drop_state_owner(struct nfs4_state_owner *sp)
>>>> -{
>>>> -       struct rb_node *rb_node = &sp->so_server_node;
>>>> -
>>>> -       if (!RB_EMPTY_NODE(rb_node)) {
>>>> -               struct nfs_server *server = sp->so_server;
>>>> -               struct nfs_client *clp = server->nfs_client;
>>>> -
>>>> -               spin_lock(&clp->cl_lock);
>>>> -               if (!RB_EMPTY_NODE(rb_node)) {
>>>> -                       rb_erase(rb_node, &server->state_owners);
>>>> -                       RB_CLEAR_NODE(rb_node);
>>>> -               }
>>>> -               spin_unlock(&clp->cl_lock);
>>>> -       }
>>>> +nfs4_reset_state_owner(struct nfs4_state_owner *sp)
>>>> +{
>>>> +       /* This state_owner is no longer usable, but must
>>>> +        * remain in place so that state recovery can find it
>>>> +        * and the opens associated with it.
>>>> +        * It may also be used for new 'open' request to
>>>> +        * return a delegation to the server.
>>>> +        * So update the 'create_time' so that it looks like
>>>> +        * a new state_owner.  This will cause the server to
>>>> +        * request an OPEN_CONFIRM to start a new sequence.
>>>> +        */
>>>> +       sp->so_seqid.create_time = ktime_get();
>>>>  }
>>>>
>>>>  static void nfs4_free_state_owner(struct nfs4_state_owner *sp)
>>>> @@ -1113,7 +1110,7 @@ void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid)
>>>>
>>>>         sp = container_of(seqid->sequence, struct nfs4_state_owner, so_seqid);
>>>>         if (status == -NFS4ERR_BAD_SEQID)
>>>> -               nfs4_drop_state_owner(sp);
>>>> +               nfs4_reset_state_owner(sp);
>>>>         if (!nfs4_has_session(sp->so_server->nfs_client))
>>>>                 nfs_increment_seqid(status, seqid);
>>>>  }
>>>> --
>>>> 2.11.0
>>>>
--
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/nfs4state.c b/fs/nfs/nfs4state.c
index cf869802ff23..1d152f4470cd 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -494,21 +494,18 @@  nfs4_alloc_state_owner(struct nfs_server *server,
 }
 
 static void
-nfs4_drop_state_owner(struct nfs4_state_owner *sp)
-{
-	struct rb_node *rb_node = &sp->so_server_node;
-
-	if (!RB_EMPTY_NODE(rb_node)) {
-		struct nfs_server *server = sp->so_server;
-		struct nfs_client *clp = server->nfs_client;
-
-		spin_lock(&clp->cl_lock);
-		if (!RB_EMPTY_NODE(rb_node)) {
-			rb_erase(rb_node, &server->state_owners);
-			RB_CLEAR_NODE(rb_node);
-		}
-		spin_unlock(&clp->cl_lock);
-	}
+nfs4_reset_state_owner(struct nfs4_state_owner *sp)
+{
+	/* This state_owner is no longer usable, but must
+	 * remain in place so that state recovery can find it
+	 * and the opens associated with it.
+	 * It may also be used for new 'open' request to
+	 * return a delegation to the server.
+	 * So update the 'create_time' so that it looks like
+	 * a new state_owner.  This will cause the server to
+	 * request an OPEN_CONFIRM to start a new sequence.
+	 */
+	sp->so_seqid.create_time = ktime_get();
 }
 
 static void nfs4_free_state_owner(struct nfs4_state_owner *sp)
@@ -1113,7 +1110,7 @@  void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid)
 
 	sp = container_of(seqid->sequence, struct nfs4_state_owner, so_seqid);
 	if (status == -NFS4ERR_BAD_SEQID)
-		nfs4_drop_state_owner(sp);
+		nfs4_reset_state_owner(sp);
 	if (!nfs4_has_session(sp->so_server->nfs_client))
 		nfs_increment_seqid(status, seqid);
 }