diff mbox

NFSv4.1: Fix client id trunking on Linux

Message ID 1420234772-13083-1-git-send-email-trond.myklebust@primarydata.com (mailing list archive)
State New, archived
Headers show

Commit Message

Trond Myklebust Jan. 2, 2015, 9:39 p.m. UTC
Currently, our trunking code will check for session trunking, but will
fail to detect client id trunking. This is a problem, because it means
that the client will fail to recognise that the two connections represent
shared state, even if they do not permit a shared session.
By removing the check for the server minor id, and only checking the
major id, we will end up doing the right thing in both cases: we close
down the new nfs_client and fall back to using the existing one.

Fixes: 05f4c350ee02e ("NFS: Discover NFSv4 server trunking when mounting")
Cc: Chuck Lever <chuck.lever@oracle.com>
Cc: stable@vger.kernel.org # 3.7.x
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
---
 fs/nfs/nfs4client.c | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)

Comments

Chuck Lever Jan. 2, 2015, 9:44 p.m. UTC | #1
On Jan 2, 2015, at 4:39 PM, Trond Myklebust <trond.myklebust@primarydata.com> wrote:

> Currently, our trunking code will check for session trunking, but will
> fail to detect client id trunking. This is a problem, because it means
> that the client will fail to recognise that the two connections represent
> shared state, even if they do not permit a shared session.
> By removing the check for the server minor id, and only checking the
> major id, we will end up doing the right thing in both cases: we close
> down the new nfs_client and fall back to using the existing one.

Fair enough.

Does this detect the case where two concurrent NFSv4.1 mounts
of the same server use different transport protocols?


> Fixes: 05f4c350ee02e ("NFS: Discover NFSv4 server trunking when mounting")
> Cc: Chuck Lever <chuck.lever@oracle.com>
> Cc: stable@vger.kernel.org # 3.7.x
> Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
> ---
> fs/nfs/nfs4client.c | 17 ++++++++---------
> 1 file changed, 8 insertions(+), 9 deletions(-)
> 
> diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
> index 03311259b0c4..d949d0f378ec 100644
> --- a/fs/nfs/nfs4client.c
> +++ b/fs/nfs/nfs4client.c
> @@ -566,20 +566,14 @@ static bool nfs4_match_clientids(struct nfs_client *a, struct nfs_client *b)
> }
> 
> /*
> - * Returns true if the server owners match
> + * Returns true if the server major ids match
>  */
> static bool
> -nfs4_match_serverowners(struct nfs_client *a, struct nfs_client *b)
> +nfs4_check_clientid_trunking(struct nfs_client *a, struct nfs_client *b)
> {
> 	struct nfs41_server_owner *o1 = a->cl_serverowner;
> 	struct nfs41_server_owner *o2 = b->cl_serverowner;
> 
> -	if (o1->minor_id != o2->minor_id) {
> -		dprintk("NFS: --> %s server owner minor IDs do not match\n",
> -			__func__);
> -		return false;
> -	}
> -
> 	if (o1->major_id_sz != o2->major_id_sz)
> 		goto out_major_mismatch;
> 	if (memcmp(o1->major_id, o2->major_id, o1->major_id_sz) != 0)
> @@ -654,7 +648,12 @@ int nfs41_walk_client_list(struct nfs_client *new,
> 		if (!nfs4_match_clientids(pos, new))
> 			continue;
> 
> -		if (!nfs4_match_serverowners(pos, new))
> +		/*
> +		 * Note that session trunking is just a special subcase of
> +		 * client id trunking. In either case, we want to fall back
> +		 * to using the existing nfs_client.
> +		 */
> +		if (!nfs4_check_clientid_trunking(pos, new))
> 			continue;
> 
> 		atomic_inc(&pos->cl_count);
> -- 
> 2.1.0
> 

--
Chuck Lever
chuck[dot]lever[at]oracle[dot]com



--
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 Jan. 2, 2015, 10:09 p.m. UTC | #2
On Fri, Jan 2, 2015 at 4:44 PM, Chuck Lever <chuck.lever@oracle.com> wrote:
>
> On Jan 2, 2015, at 4:39 PM, Trond Myklebust <trond.myklebust@primarydata.com> wrote:
>
>> Currently, our trunking code will check for session trunking, but will
>> fail to detect client id trunking. This is a problem, because it means
>> that the client will fail to recognise that the two connections represent
>> shared state, even if they do not permit a shared session.
>> By removing the check for the server minor id, and only checking the
>> major id, we will end up doing the right thing in both cases: we close
>> down the new nfs_client and fall back to using the existing one.
>
> Fair enough.
>
> Does this detect the case where two concurrent NFSv4.1 mounts
> of the same server use different transport protocols?

I'm not sure I understand what you mean. The client should completely
ignore the transport protocol when doing trunking detection. The only
thing that it cares about is whether or not the server believes we
should be sharing state, in which case the client should fall back to
using the existing connection for the new mount point.

>> Fixes: 05f4c350ee02e ("NFS: Discover NFSv4 server trunking when mounting")
>> Cc: Chuck Lever <chuck.lever@oracle.com>
>> Cc: stable@vger.kernel.org # 3.7.x
>> Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
>> ---
>> fs/nfs/nfs4client.c | 17 ++++++++---------
>> 1 file changed, 8 insertions(+), 9 deletions(-)
>>
>> diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
>> index 03311259b0c4..d949d0f378ec 100644
>> --- a/fs/nfs/nfs4client.c
>> +++ b/fs/nfs/nfs4client.c
>> @@ -566,20 +566,14 @@ static bool nfs4_match_clientids(struct nfs_client *a, struct nfs_client *b)
>> }
>>
>> /*
>> - * Returns true if the server owners match
>> + * Returns true if the server major ids match
>>  */
>> static bool
>> -nfs4_match_serverowners(struct nfs_client *a, struct nfs_client *b)
>> +nfs4_check_clientid_trunking(struct nfs_client *a, struct nfs_client *b)
>> {
>>       struct nfs41_server_owner *o1 = a->cl_serverowner;
>>       struct nfs41_server_owner *o2 = b->cl_serverowner;
>>
>> -     if (o1->minor_id != o2->minor_id) {
>> -             dprintk("NFS: --> %s server owner minor IDs do not match\n",
>> -                     __func__);
>> -             return false;
>> -     }
>> -
>>       if (o1->major_id_sz != o2->major_id_sz)
>>               goto out_major_mismatch;
>>       if (memcmp(o1->major_id, o2->major_id, o1->major_id_sz) != 0)
>> @@ -654,7 +648,12 @@ int nfs41_walk_client_list(struct nfs_client *new,
>>               if (!nfs4_match_clientids(pos, new))
>>                       continue;
>>
>> -             if (!nfs4_match_serverowners(pos, new))
>> +             /*
>> +              * Note that session trunking is just a special subcase of
>> +              * client id trunking. In either case, we want to fall back
>> +              * to using the existing nfs_client.
>> +              */
>> +             if (!nfs4_check_clientid_trunking(pos, new))
>>                       continue;
>>
>>               atomic_inc(&pos->cl_count);
>> --
>> 2.1.0
>>
>
> --
> Chuck Lever
> chuck[dot]lever[at]oracle[dot]com
>
>
>
Chuck Lever Jan. 2, 2015, 11:36 p.m. UTC | #3
On Jan 2, 2015, at 5:09 PM, Trond Myklebust <trond.myklebust@primarydata.com> wrote:

> On Fri, Jan 2, 2015 at 4:44 PM, Chuck Lever <chuck.lever@oracle.com> wrote:
>> 
>> On Jan 2, 2015, at 4:39 PM, Trond Myklebust <trond.myklebust@primarydata.com> wrote:
>> 
>>> Currently, our trunking code will check for session trunking, but will
>>> fail to detect client id trunking. This is a problem, because it means
>>> that the client will fail to recognise that the two connections represent
>>> shared state, even if they do not permit a shared session.
>>> By removing the check for the server minor id, and only checking the
>>> major id, we will end up doing the right thing in both cases: we close
>>> down the new nfs_client and fall back to using the existing one.
>> 
>> Fair enough.
>> 
>> Does this detect the case where two concurrent NFSv4.1 mounts
>> of the same server use different transport protocols?
> 
> I'm not sure I understand what you mean. The client should completely
> ignore the transport protocol when doing trunking detection.

Quite right, the problem seems to be earlier than that.

nfs_match_client() checks the transport protocol setting, and
forces creation of a new struct nfs_client if the transport
protocol is not the same as an existing and otherwise compatible
struct nfs_client for the server being mounted.

OK for NFSv2 and NFSv3, which can have a unique struct nfs_clients
each for UDP, TCP, and RDMA mounts.

For NFSv4, RDMA means the nfs_match_client() check forces the
creation of a separate struct nfs_client for RDMA and TCP (if
admin requests both types of mounts).

An RDMA connection and a TCP connection would be created. Both
connections would send the same nfs_client_id4.

This doesn’t seem to be an issue for non-UCS NFSv4.0, but it
probably is a problem with UCS.

> The only
> thing that it cares about is whether or not the server believes we
> should be sharing state, in which case the client should fall back to
> using the existing connection for the new mount point.

While working on the NFSv4.1 on RDMA prototype, I noticed that
trunking detection would allow concurrent mounts of the same
server with both RDMA and TCP transports. My hack-neyed attempt
to fix that was posted back in October:

  http://marc.info/?l=linux-nfs&m=141348837927749&w=2

Your patch very likely addresses the issue for NFSv4.1, but I
wonder how to do it for NFSv4.0 with UCS (if it is a problem,
at the time I think I only checked NFSv4.1 behavior).


>>> Fixes: 05f4c350ee02e ("NFS: Discover NFSv4 server trunking when mounting")
>>> Cc: Chuck Lever <chuck.lever@oracle.com>
>>> Cc: stable@vger.kernel.org # 3.7.x
>>> Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
>>> ---
>>> fs/nfs/nfs4client.c | 17 ++++++++---------
>>> 1 file changed, 8 insertions(+), 9 deletions(-)
>>> 
>>> diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
>>> index 03311259b0c4..d949d0f378ec 100644
>>> --- a/fs/nfs/nfs4client.c
>>> +++ b/fs/nfs/nfs4client.c
>>> @@ -566,20 +566,14 @@ static bool nfs4_match_clientids(struct nfs_client *a, struct nfs_client *b)
>>> }
>>> 
>>> /*
>>> - * Returns true if the server owners match
>>> + * Returns true if the server major ids match
>>> */
>>> static bool
>>> -nfs4_match_serverowners(struct nfs_client *a, struct nfs_client *b)
>>> +nfs4_check_clientid_trunking(struct nfs_client *a, struct nfs_client *b)
>>> {
>>>      struct nfs41_server_owner *o1 = a->cl_serverowner;
>>>      struct nfs41_server_owner *o2 = b->cl_serverowner;
>>> 
>>> -     if (o1->minor_id != o2->minor_id) {
>>> -             dprintk("NFS: --> %s server owner minor IDs do not match\n",
>>> -                     __func__);
>>> -             return false;
>>> -     }
>>> -
>>>      if (o1->major_id_sz != o2->major_id_sz)
>>>              goto out_major_mismatch;
>>>      if (memcmp(o1->major_id, o2->major_id, o1->major_id_sz) != 0)
>>> @@ -654,7 +648,12 @@ int nfs41_walk_client_list(struct nfs_client *new,
>>>              if (!nfs4_match_clientids(pos, new))
>>>                      continue;
>>> 
>>> -             if (!nfs4_match_serverowners(pos, new))
>>> +             /*
>>> +              * Note that session trunking is just a special subcase of
>>> +              * client id trunking. In either case, we want to fall back
>>> +              * to using the existing nfs_client.
>>> +              */
>>> +             if (!nfs4_check_clientid_trunking(pos, new))
>>>                      continue;
>>> 
>>>              atomic_inc(&pos->cl_count);
>>> --
>>> 2.1.0
>>> 
>> 
>> --
>> Chuck Lever
>> chuck[dot]lever[at]oracle[dot]com
>> 
>> 
>> 
> 
> 
> 
> -- 
> Trond Myklebust
> Linux NFS client maintainer, PrimaryData
> trond.myklebust@primarydata.com
> --
> 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

--
Chuck Lever
chuck[dot]lever[at]oracle[dot]com



--
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
Chuck Lever Jan. 3, 2015, 7:45 p.m. UTC | #4
On Jan 2, 2015, at 4:39 PM, Trond Myklebust <trond.myklebust@primarydata.com> wrote:

> Currently, our trunking code will check for session trunking, but will
> fail to detect client id trunking. This is a problem, because it means
> that the client will fail to recognise that the two connections represent
> shared state, even if they do not permit a shared session.
> By removing the check for the server minor id, and only checking the
> major id, we will end up doing the right thing in both cases: we close
> down the new nfs_client and fall back to using the existing one.
> 
> Fixes: 05f4c350ee02e ("NFS: Discover NFSv4 server trunking when mounting")
> Cc: Chuck Lever <chuck.lever@oracle.com>
> Cc: stable@vger.kernel.org # 3.7.x
> Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>

You can add my Tested-by or Reviewed-by if you like.


> ---
> fs/nfs/nfs4client.c | 17 ++++++++---------
> 1 file changed, 8 insertions(+), 9 deletions(-)
> 
> diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
> index 03311259b0c4..d949d0f378ec 100644
> --- a/fs/nfs/nfs4client.c
> +++ b/fs/nfs/nfs4client.c
> @@ -566,20 +566,14 @@ static bool nfs4_match_clientids(struct nfs_client *a, struct nfs_client *b)
> }
> 
> /*
> - * Returns true if the server owners match
> + * Returns true if the server major ids match
>  */
> static bool
> -nfs4_match_serverowners(struct nfs_client *a, struct nfs_client *b)
> +nfs4_check_clientid_trunking(struct nfs_client *a, struct nfs_client *b)
> {
> 	struct nfs41_server_owner *o1 = a->cl_serverowner;
> 	struct nfs41_server_owner *o2 = b->cl_serverowner;
> 
> -	if (o1->minor_id != o2->minor_id) {
> -		dprintk("NFS: --> %s server owner minor IDs do not match\n",
> -			__func__);
> -		return false;
> -	}
> -
> 	if (o1->major_id_sz != o2->major_id_sz)
> 		goto out_major_mismatch;
> 	if (memcmp(o1->major_id, o2->major_id, o1->major_id_sz) != 0)
> @@ -654,7 +648,12 @@ int nfs41_walk_client_list(struct nfs_client *new,
> 		if (!nfs4_match_clientids(pos, new))
> 			continue;
> 
> -		if (!nfs4_match_serverowners(pos, new))
> +		/*
> +		 * Note that session trunking is just a special subcase of
> +		 * client id trunking. In either case, we want to fall back
> +		 * to using the existing nfs_client.
> +		 */
> +		if (!nfs4_check_clientid_trunking(pos, new))
> 			continue;
> 
> 		atomic_inc(&pos->cl_count);
> -- 
> 2.1.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

--
Chuck Lever
chuck[dot]lever[at]oracle[dot]com



--
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 Jan. 3, 2015, 9:19 p.m. UTC | #5
On Fri, Jan 2, 2015 at 6:36 PM, Chuck Lever <chuck.lever@oracle.com> wrote:
>
> On Jan 2, 2015, at 5:09 PM, Trond Myklebust <trond.myklebust@primarydata.com> wrote:
>
>> On Fri, Jan 2, 2015 at 4:44 PM, Chuck Lever <chuck.lever@oracle.com> wrote:
>>>
>>> On Jan 2, 2015, at 4:39 PM, Trond Myklebust <trond.myklebust@primarydata.com> wrote:
>>>
>>>> Currently, our trunking code will check for session trunking, but will
>>>> fail to detect client id trunking. This is a problem, because it means
>>>> that the client will fail to recognise that the two connections represent
>>>> shared state, even if they do not permit a shared session.
>>>> By removing the check for the server minor id, and only checking the
>>>> major id, we will end up doing the right thing in both cases: we close
>>>> down the new nfs_client and fall back to using the existing one.
>>>
>>> Fair enough.
>>>
>>> Does this detect the case where two concurrent NFSv4.1 mounts
>>> of the same server use different transport protocols?
>>
>> I'm not sure I understand what you mean. The client should completely
>> ignore the transport protocol when doing trunking detection.
>
> Quite right, the problem seems to be earlier than that.
>
> nfs_match_client() checks the transport protocol setting, and
> forces creation of a new struct nfs_client if the transport
> protocol is not the same as an existing and otherwise compatible
> struct nfs_client for the server being mounted.
>
> OK for NFSv2 and NFSv3, which can have a unique struct nfs_clients
> each for UDP, TCP, and RDMA mounts.
>
> For NFSv4, RDMA means the nfs_match_client() check forces the
> creation of a separate struct nfs_client for RDMA and TCP (if
> admin requests both types of mounts).
>
> An RDMA connection and a TCP connection would be created. Both
> connections would send the same nfs_client_id4.
>
> This doesn’t seem to be an issue for non-UCS NFSv4.0, but it
> probably is a problem with UCS.

The problem isn't so much the check in nfs_match_client(). The problem
is rather those same checks in nfs4[01]_walk_client_list().

>> The only
>> thing that it cares about is whether or not the server believes we
>> should be sharing state, in which case the client should fall back to
>> using the existing connection for the new mount point.
>
> While working on the NFSv4.1 on RDMA prototype, I noticed that
> trunking detection would allow concurrent mounts of the same
> server with both RDMA and TCP transports. My hack-neyed attempt
> to fix that was posted back in October:
>
>   http://marc.info/?l=linux-nfs&m=141348837927749&w=2
>
> Your patch very likely addresses the issue for NFSv4.1, but I
> wonder how to do it for NFSv4.0 with UCS (if it is a problem,
> at the time I think I only checked NFSv4.1 behavior).

There is a third problem, and that is what if someone plays with
-omigration or /sys/module/nfs/parameters/nfs4_unique_id after the
server is already mounted. We know that the server will not identify
us as being the same client in that case, and so we should try to
ascertain that we don't create any false positives.
The new patchset (v2) therefore attempts to address all of the above issues.
diff mbox

Patch

diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
index 03311259b0c4..d949d0f378ec 100644
--- a/fs/nfs/nfs4client.c
+++ b/fs/nfs/nfs4client.c
@@ -566,20 +566,14 @@  static bool nfs4_match_clientids(struct nfs_client *a, struct nfs_client *b)
 }
 
 /*
- * Returns true if the server owners match
+ * Returns true if the server major ids match
  */
 static bool
-nfs4_match_serverowners(struct nfs_client *a, struct nfs_client *b)
+nfs4_check_clientid_trunking(struct nfs_client *a, struct nfs_client *b)
 {
 	struct nfs41_server_owner *o1 = a->cl_serverowner;
 	struct nfs41_server_owner *o2 = b->cl_serverowner;
 
-	if (o1->minor_id != o2->minor_id) {
-		dprintk("NFS: --> %s server owner minor IDs do not match\n",
-			__func__);
-		return false;
-	}
-
 	if (o1->major_id_sz != o2->major_id_sz)
 		goto out_major_mismatch;
 	if (memcmp(o1->major_id, o2->major_id, o1->major_id_sz) != 0)
@@ -654,7 +648,12 @@  int nfs41_walk_client_list(struct nfs_client *new,
 		if (!nfs4_match_clientids(pos, new))
 			continue;
 
-		if (!nfs4_match_serverowners(pos, new))
+		/*
+		 * Note that session trunking is just a special subcase of
+		 * client id trunking. In either case, we want to fall back
+		 * to using the existing nfs_client.
+		 */
+		if (!nfs4_check_clientid_trunking(pos, new))
 			continue;
 
 		atomic_inc(&pos->cl_count);