2016-07-27 18:41:21

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 7 0/8] pNFS file layout session trunking

From: Andy Adamson <[email protected]>

This patch set differs from previous session trunking patch sets in that it
only addresses pNFS filelayout data server session trunking.

Andy Adamson (8):
NFS setup async exchange_id
NFS refactor nfs4_match_clientids
NFS refactor nfs4_check_serverowner_major_id
NFS detect session trunking
SUNRPC add flag to rpc_task_release_client
SUNRPC add an RPC null call to test session trunking connection
NFS test session trunking with exchange id
NFS pnfs data server multipath session trunking

fs/nfs/nfs4_fs.h | 9 ++
fs/nfs/nfs4client.c | 114 ++++++++++++++--
fs/nfs/nfs4proc.c | 310 ++++++++++++++++++++++++++++++++------------
fs/nfs/pnfs_nfs.c | 52 ++++++--
include/linux/sunrpc/clnt.h | 4 +-
net/sunrpc/clnt.c | 24 +++-
net/sunrpc/sched.c | 2 +-
net/sunrpc/xprtmultipath.c | 2 +
8 files changed, 402 insertions(+), 115 deletions(-)

--
1.8.3.1



2016-07-27 18:41:23

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 7 2/8] NFS refactor nfs4_match_clientids

From: Andy Adamson <[email protected]>

For session trunking, to compare nfs41_exchange_id_res with
exiting nfs_client.

Signed-off-by: Andy Adamson <[email protected]>
---
fs/nfs/nfs4client.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
index 8d7d08d..d77a1ad 100644
--- a/fs/nfs/nfs4client.c
+++ b/fs/nfs/nfs4client.c
@@ -562,15 +562,15 @@ out:
/*
* Returns true if the client IDs match
*/
-static bool nfs4_match_clientids(struct nfs_client *a, struct nfs_client *b)
+static bool nfs4_match_clientids(u64 a, u64 b)
{
- if (a->cl_clientid != b->cl_clientid) {
+ if (a != b) {
dprintk("NFS: --> %s client ID %llx does not match %llx\n",
- __func__, a->cl_clientid, b->cl_clientid);
+ __func__, a, b);
return false;
}
dprintk("NFS: --> %s client ID %llx matches %llx\n",
- __func__, a->cl_clientid, b->cl_clientid);
+ __func__, a, b);
return true;
}

@@ -650,7 +650,7 @@ int nfs41_walk_client_list(struct nfs_client *new,
if (pos->cl_cons_state != NFS_CS_READY)
continue;

- if (!nfs4_match_clientids(pos, new))
+ if (!nfs4_match_clientids(pos->cl_clientid, new->cl_clientid))
continue;

/*
--
1.8.3.1


2016-07-27 18:41:22

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 7 1/8] NFS setup async exchange_id

From: Andy Adamson <[email protected]>

Testing an rpc_xprt for session trunking should not delay application
progress over already established transports.
Setup exchange_id to be able to be an async call to test an rpc_xprt
for session trunking use.

Signed-off-by: Andy Adamson <[email protected]>
---
fs/nfs/nfs4proc.c | 215 ++++++++++++++++++++++++++++++++++--------------------
1 file changed, 134 insertions(+), 81 deletions(-)

diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index da5c9e5..e29f5ce 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -7051,6 +7051,80 @@ static int nfs4_sp4_select_mode(struct nfs_client *clp,
return 0;
}

+struct nfs41_exchange_id_data {
+ struct nfs41_exchange_id_res res;
+ struct nfs41_exchange_id_args args;
+ int rpc_status;
+};
+
+static void nfs4_exchange_id_done(struct rpc_task *task, void *data)
+{
+ struct nfs41_exchange_id_data *cdata =
+ (struct nfs41_exchange_id_data *)data;
+ struct nfs_client *clp = cdata->args.client;
+ int status = task->tk_status;
+
+ trace_nfs4_exchange_id(clp, status);
+
+ if (status == 0)
+ status = nfs4_check_cl_exchange_flags(cdata->res.flags);
+ if (status == 0)
+ status = nfs4_sp4_select_mode(clp, &cdata->res.state_protect);
+
+ if (status == 0) {
+ clp->cl_clientid = cdata->res.clientid;
+ clp->cl_exchange_flags = cdata->res.flags;
+ /* Client ID is not confirmed */
+ if (!(cdata->res.flags & EXCHGID4_FLAG_CONFIRMED_R)) {
+ clear_bit(NFS4_SESSION_ESTABLISHED,
+ &clp->cl_session->session_state);
+ clp->cl_seqid = cdata->res.seqid;
+ }
+
+ kfree(clp->cl_serverowner);
+ clp->cl_serverowner = cdata->res.server_owner;
+ cdata->res.server_owner = NULL;
+
+ /* use the most recent implementation id */
+ kfree(clp->cl_implid);
+ clp->cl_implid = cdata->res.impl_id;
+ cdata->res.impl_id = NULL;
+
+ if (clp->cl_serverscope != NULL &&
+ !nfs41_same_server_scope(clp->cl_serverscope,
+ cdata->res.server_scope)) {
+ dprintk("%s: server_scope mismatch detected\n",
+ __func__);
+ set_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, &clp->cl_state);
+ kfree(clp->cl_serverscope);
+ clp->cl_serverscope = NULL;
+ }
+
+ if (clp->cl_serverscope == NULL) {
+ clp->cl_serverscope = cdata->res.server_scope;
+ cdata->res.server_scope = NULL;
+ }
+ }
+ cdata->rpc_status = status;
+}
+
+static void nfs4_exchange_id_release(void *data)
+{
+ struct nfs41_exchange_id_data *cdata =
+ (struct nfs41_exchange_id_data *)data;
+
+ nfs_put_client(cdata->args.client);
+ kfree(cdata->res.impl_id);
+ kfree(cdata->res.server_scope);
+ kfree(cdata->res.server_owner);
+ kfree(cdata);
+}
+
+static const struct rpc_call_ops nfs4_exchange_id_call_ops = {
+ .rpc_call_done = nfs4_exchange_id_done,
+ .rpc_release = nfs4_exchange_id_release,
+};
+
/*
* _nfs4_proc_exchange_id()
*
@@ -7060,66 +7134,60 @@ static int _nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred,
u32 sp4_how)
{
nfs4_verifier verifier;
- struct nfs41_exchange_id_args args = {
- .verifier = &verifier,
- .client = clp,
-#ifdef CONFIG_NFS_V4_1_MIGRATION
- .flags = EXCHGID4_FLAG_SUPP_MOVED_REFER |
- EXCHGID4_FLAG_BIND_PRINC_STATEID |
- EXCHGID4_FLAG_SUPP_MOVED_MIGR,
-#else
- .flags = EXCHGID4_FLAG_SUPP_MOVED_REFER |
- EXCHGID4_FLAG_BIND_PRINC_STATEID,
-#endif
- };
- struct nfs41_exchange_id_res res = {
- 0
- };
- int status;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_EXCHANGE_ID],
- .rpc_argp = &args,
- .rpc_resp = &res,
.rpc_cred = cred,
};
+ struct rpc_task_setup task_setup_data = {
+ .rpc_client = clp->cl_rpcclient,
+ .callback_ops = &nfs4_exchange_id_call_ops,
+ .rpc_message = &msg,
+ .flags = RPC_TASK_ASYNC | RPC_TASK_TIMEOUT,
+ };
+ struct nfs41_exchange_id_data *calldata;
+ struct rpc_task *task;
+ int status = -EIO;
+
+ if (!atomic_inc_not_zero(&clp->cl_count))
+ goto out;
+
+ status = -ENOMEM;
+ calldata = kzalloc(sizeof(*calldata), GFP_NOFS);
+ if (!calldata)
+ goto out;

nfs4_init_boot_verifier(clp, &verifier);

status = nfs4_init_uniform_client_string(clp);
if (status)
- goto out;
+ goto out_calldata;

dprintk("NFS call exchange_id auth=%s, '%s'\n",
clp->cl_rpcclient->cl_auth->au_ops->au_name,
clp->cl_owner_id);

- res.server_owner = kzalloc(sizeof(struct nfs41_server_owner),
- GFP_NOFS);
- if (unlikely(res.server_owner == NULL)) {
- status = -ENOMEM;
- goto out;
- }
+ calldata->res.server_owner = kzalloc(sizeof(struct nfs41_server_owner),
+ GFP_NOFS);
+ status = -ENOMEM;
+ if (unlikely(calldata->res.server_owner == NULL))
+ goto out_calldata;

- res.server_scope = kzalloc(sizeof(struct nfs41_server_scope),
+ calldata->res.server_scope = kzalloc(sizeof(struct nfs41_server_scope),
GFP_NOFS);
- if (unlikely(res.server_scope == NULL)) {
- status = -ENOMEM;
+ if (unlikely(calldata->res.server_scope == NULL))
goto out_server_owner;
- }

- res.impl_id = kzalloc(sizeof(struct nfs41_impl_id), GFP_NOFS);
- if (unlikely(res.impl_id == NULL)) {
- status = -ENOMEM;
+ calldata->res.impl_id = kzalloc(sizeof(struct nfs41_impl_id), GFP_NOFS);
+ if (unlikely(calldata->res.impl_id == NULL))
goto out_server_scope;
- }

switch (sp4_how) {
case SP4_NONE:
- args.state_protect.how = SP4_NONE;
+ calldata->args.state_protect.how = SP4_NONE;
break;

case SP4_MACH_CRED:
- args.state_protect = nfs4_sp4_mach_cred_request;
+ calldata->args.state_protect = nfs4_sp4_mach_cred_request;
break;

default:
@@ -7129,55 +7197,30 @@ static int _nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred,
goto out_impl_id;
}

- status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
- trace_nfs4_exchange_id(clp, status);
- if (status == 0)
- status = nfs4_check_cl_exchange_flags(res.flags);
-
- if (status == 0)
- status = nfs4_sp4_select_mode(clp, &res.state_protect);
-
- if (status == 0) {
- clp->cl_clientid = res.clientid;
- clp->cl_exchange_flags = res.flags;
- /* Client ID is not confirmed */
- if (!(res.flags & EXCHGID4_FLAG_CONFIRMED_R)) {
- clear_bit(NFS4_SESSION_ESTABLISHED,
- &clp->cl_session->session_state);
- clp->cl_seqid = res.seqid;
- }
-
- kfree(clp->cl_serverowner);
- clp->cl_serverowner = res.server_owner;
- res.server_owner = NULL;
-
- /* use the most recent implementation id */
- kfree(clp->cl_implid);
- clp->cl_implid = res.impl_id;
- res.impl_id = NULL;
-
- if (clp->cl_serverscope != NULL &&
- !nfs41_same_server_scope(clp->cl_serverscope,
- res.server_scope)) {
- dprintk("%s: server_scope mismatch detected\n",
- __func__);
- set_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, &clp->cl_state);
- kfree(clp->cl_serverscope);
- clp->cl_serverscope = NULL;
- }
+ calldata->args.verifier = &verifier;
+ calldata->args.client = clp;
+#ifdef CONFIG_NFS_V4_1_MIGRATION
+ calldata->args.flags = EXCHGID4_FLAG_SUPP_MOVED_REFER |
+ EXCHGID4_FLAG_BIND_PRINC_STATEID |
+ EXCHGID4_FLAG_SUPP_MOVED_MIGR,
+#else
+ calldata->args.flags = EXCHGID4_FLAG_SUPP_MOVED_REFER |
+ EXCHGID4_FLAG_BIND_PRINC_STATEID,
+#endif
+ msg.rpc_argp = &calldata->args;
+ msg.rpc_resp = &calldata->res;
+ task_setup_data.callback_data = calldata;

- if (clp->cl_serverscope == NULL) {
- clp->cl_serverscope = res.server_scope;
- res.server_scope = NULL;
- }
+ task = rpc_run_task(&task_setup_data);
+ if (IS_ERR(task)) {
+ status = PTR_ERR(task);
+ goto out_impl_id;
}

-out_impl_id:
- kfree(res.impl_id);
-out_server_scope:
- kfree(res.server_scope);
-out_server_owner:
- kfree(res.server_owner);
+ status = rpc_wait_for_completion_task(task);
+ if (!status)
+ status = calldata->rpc_status;
+ rpc_put_task(task);
out:
if (clp->cl_implid != NULL)
dprintk("NFS reply exchange_id: Server Implementation ID: "
@@ -7187,6 +7230,16 @@ out:
clp->cl_implid->date.nseconds);
dprintk("NFS reply exchange_id: %d\n", status);
return status;
+
+out_impl_id:
+ kfree(calldata->res.impl_id);
+out_server_scope:
+ kfree(calldata->res.server_scope);
+out_server_owner:
+ kfree(calldata->res.server_owner);
+out_calldata:
+ kfree(calldata);
+ goto out;
}

/*
--
1.8.3.1


2016-07-27 18:41:23

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 7 3/8] NFS refactor nfs4_check_serverowner_major_id

From: Andy Adamson <[email protected]>

For session trunking, to compare nfs41_exchange_id_res with
existing nfs_client

Signed-off-by: Andy Adamson <[email protected]>
---
fs/nfs/nfs4client.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
index d77a1ad..f356d50 100644
--- a/fs/nfs/nfs4client.c
+++ b/fs/nfs/nfs4client.c
@@ -578,17 +578,15 @@ static bool nfs4_match_clientids(u64 a, u64 b)
* Returns true if the server major ids match
*/
static bool
-nfs4_check_clientid_trunking(struct nfs_client *a, struct nfs_client *b)
+nfs4_check_serverowner_major_id(struct nfs41_server_owner *o1,
+ struct nfs41_server_owner *o2)
{
- struct nfs41_server_owner *o1 = a->cl_serverowner;
- struct nfs41_server_owner *o2 = b->cl_serverowner;
-
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)
goto out_major_mismatch;

- dprintk("NFS: --> %s server owners match\n", __func__);
+ dprintk("NFS: --> %s server owner major IDs match\n", __func__);
return true;

out_major_mismatch:
@@ -658,7 +656,8 @@ int nfs41_walk_client_list(struct nfs_client *new,
* client id trunking. In either case, we want to fall back
* to using the existing nfs_client.
*/
- if (!nfs4_check_clientid_trunking(pos, new))
+ if (!nfs4_check_serverowner_major_id(pos->cl_serverowner,
+ new->cl_serverowner))
continue;

/* Unlike NFSv4.0, we know that NFSv4.1 always uses the
--
1.8.3.1


2016-07-27 18:41:24

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 7 4/8] NFS detect session trunking

From: Andy Adamson <[email protected]>

Signed-off-by: Andy Adamson <[email protected]>
---
fs/nfs/nfs4_fs.h | 2 ++
fs/nfs/nfs4client.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 95 insertions(+)

diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index 4be567a..eb315e1 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -277,6 +277,8 @@ extern int nfs4_proc_get_lease_time(struct nfs_client *clp,
struct nfs_fsinfo *fsinfo);
extern int nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data,
bool sync);
+extern int nfs4_detect_session_trunking(struct nfs_client *clp,
+ struct nfs41_exchange_id_res *res, struct rpc_xprt *xprt);

static inline bool
is_ds_only_client(struct nfs_client *clp)
diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
index f356d50..37a9416 100644
--- a/fs/nfs/nfs4client.c
+++ b/fs/nfs/nfs4client.c
@@ -595,6 +595,99 @@ out_major_mismatch:
return false;
}

+/*
+ * Returns true if server minor ids match
+ */
+static bool
+nfs4_check_serverowner_minor_id(struct nfs41_server_owner *o1,
+ struct nfs41_server_owner *o2)
+{
+ /* Check eir_server_owner so_minor_id */
+ if (o1->minor_id != o2->minor_id)
+ goto out_minor_mismatch;
+
+ dprintk("NFS: --> %s server owner minor IDs s match\n", __func__);
+ return true;
+
+out_minor_mismatch:
+ dprintk("NFS: --> %s server owner minor IDs do not match\n", __func__);
+ return false;
+}
+
+/*
+ * Returns true if the server scopes match
+ */
+static bool
+nfs4_check_server_scope(struct nfs41_server_scope *s1,
+ struct nfs41_server_scope *s2)
+{
+ if (s1->server_scope_sz != s2->server_scope_sz)
+ goto out_scope_mismatch;
+ if (memcmp(s1->server_scope, s2->server_scope,
+ s1->server_scope_sz) != 0)
+ goto out_scope_mismatch;
+
+ dprintk("NFS: --> %s server scopes match\n", __func__);
+ return true;
+
+out_scope_mismatch:
+ dprintk("NFS: --> %s server scopes do not match\n",
+ __func__);
+ return false;
+}
+
+/**
+ * nfs4_detect_session_trunking - Checks for ssession trunking called
+ * after a successuful EXCHANGE_ID testing a multi-addr connection to be
+ * potentially added as a session trunk
+ *
+ * @clp: original mount nfs_client
+ * @res: result structure from an exchange_id using the original mount
+ * nfs_client with a new multi_addr transport
+ *
+ * Returns zero on success, otherwise -EINVAL
+ *
+ * Note: since the exchange_id for the new multi_addr transport uses the
+ * same nfs_client from the original mount, the cl_owner_id is reused,
+ * so eir_clientowner is the same.
+ */
+int nfs4_detect_session_trunking(struct nfs_client *clp,
+ struct nfs41_exchange_id_res *res,
+ struct rpc_xprt *xprt)
+{
+ int status = -EINVAL;
+ /* Check eir_clientid */
+ if (!nfs4_match_clientids(clp->cl_clientid, res->clientid))
+ goto out;
+
+ /* Check eir_server_owner so_major_id */
+ if (!nfs4_check_serverowner_major_id(clp->cl_serverowner,
+ res->server_owner))
+ goto out;
+
+ /* Check eir_server_owner so_minor_id */
+ if (!nfs4_check_serverowner_minor_id(clp->cl_serverowner,
+ res->server_owner))
+ goto out;
+
+ /* Check eir_server_scope */
+ if (!nfs4_check_server_scope(clp->cl_serverscope, res->server_scope))
+ goto out;
+
+ status = 0;
+out:
+ if (status)
+ pr_info("NFS: %s: Session trunking failed for %s status %d\n",
+ clp->cl_hostname,
+ xprt->address_strings[RPC_DISPLAY_ADDR], status);
+ else
+ pr_info("NFS: %s: Session trunking succeeded for %s\n",
+ clp->cl_hostname,
+ xprt->address_strings[RPC_DISPLAY_ADDR]);
+
+ return status;
+}
+
/**
* nfs41_walk_client_list - Find nfs_client that matches a client/server owner
*
--
1.8.3.1


2016-07-27 18:41:24

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 7 5/8] SUNRPC add flag to rpc_task_release_client

From: Andy Adamson <[email protected]>

Want to specify which rpc_xprt to use in rpc_run_task.

Don't pass in an rpc_xprt in rpc_init_task just to have it not used as it
is removed in rpc_task_release_client.

Signed-off-by: Andy Adamson <[email protected]>
---
include/linux/sunrpc/clnt.h | 2 +-
net/sunrpc/clnt.c | 6 +++---
net/sunrpc/sched.c | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
index b6810c9..99410bb 100644
--- a/include/linux/sunrpc/clnt.h
+++ b/include/linux/sunrpc/clnt.h
@@ -148,7 +148,7 @@ int rpc_switch_client_transport(struct rpc_clnt *,

void rpc_shutdown_client(struct rpc_clnt *);
void rpc_release_client(struct rpc_clnt *);
-void rpc_task_release_client(struct rpc_task *);
+void rpc_task_release_client(struct rpc_task *, int);

int rpcb_create_local(struct net *);
void rpcb_put_local(struct net *);
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index cb49898..08f3e9e 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -961,7 +961,7 @@ out:
}
EXPORT_SYMBOL_GPL(rpc_bind_new_program);

-void rpc_task_release_client(struct rpc_task *task)
+void rpc_task_release_client(struct rpc_task *task, int rm_xprt)
{
struct rpc_clnt *clnt = task->tk_client;
struct rpc_xprt *xprt = task->tk_xprt;
@@ -976,7 +976,7 @@ void rpc_task_release_client(struct rpc_task *task)
rpc_release_client(clnt);
}

- if (xprt != NULL) {
+ if (rm_xprt && xprt) {
task->tk_xprt = NULL;

xprt_put(xprt);
@@ -988,7 +988,7 @@ void rpc_task_set_client(struct rpc_task *task, struct rpc_clnt *clnt)
{

if (clnt != NULL) {
- rpc_task_release_client(task);
+ rpc_task_release_client(task, 0);
if (task->tk_xprt == NULL)
task->tk_xprt = xprt_iter_get_next(&clnt->cl_xpi);
task->tk_client = clnt;
diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c
index 9ae5885..575b254 100644
--- a/net/sunrpc/sched.c
+++ b/net/sunrpc/sched.c
@@ -1017,7 +1017,7 @@ static void rpc_release_resources_task(struct rpc_task *task)
put_rpccred(task->tk_msg.rpc_cred);
task->tk_msg.rpc_cred = NULL;
}
- rpc_task_release_client(task);
+ rpc_task_release_client(task, 1);
}

static void rpc_final_put_task(struct rpc_task *task,
--
1.8.3.1


2016-07-27 18:41:26

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 7 7/8] NFS test session trunking with exchange id

From: Andy Adamson <[email protected]>

Signed-off-by: Andy Adamson <[email protected]>
---
fs/nfs/nfs4_fs.h | 5 +++
fs/nfs/nfs4proc.c | 97 +++++++++++++++++++++++++++++++++++++++++++---
net/sunrpc/xprtmultipath.c | 2 +
3 files changed, 98 insertions(+), 6 deletions(-)

diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index eb315e1..b2a94ca 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -203,6 +203,11 @@ struct nfs4_state_recovery_ops {
struct rpc_cred *);
};

+struct nfs4_add_xprt_data {
+ struct nfs_client *clp;
+ struct rpc_cred *cred;
+};
+
struct nfs4_state_maintenance_ops {
int (*sched_state_renewal)(struct nfs_client *, struct rpc_cred *, unsigned);
struct rpc_cred * (*get_state_renewal_cred_locked)(struct nfs_client *);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index e29f5ce..fa9ae75 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -44,6 +44,7 @@
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/addr.h>
#include <linux/nfs.h>
#include <linux/nfs4.h>
#include <linux/nfs_fs.h>
@@ -7050,10 +7051,16 @@ static int nfs4_sp4_select_mode(struct nfs_client *clp,

return 0;
}
+struct nfs41_test_xprt_data {
+ struct rpc_xprt *xprt;
+ struct rpc_xprt_switch *xps;
+};

struct nfs41_exchange_id_data {
struct nfs41_exchange_id_res res;
struct nfs41_exchange_id_args args;
+ struct nfs41_test_xprt_data xdata;
+ int session_trunk;
int rpc_status;
};

@@ -7068,6 +7075,10 @@ static void nfs4_exchange_id_done(struct rpc_task *task, void *data)

if (status == 0)
status = nfs4_check_cl_exchange_flags(cdata->res.flags);
+
+ if (cdata->session_trunk)
+ goto session_trunk;
+
if (status == 0)
status = nfs4_sp4_select_mode(clp, &cdata->res.state_protect);

@@ -7105,7 +7116,15 @@ static void nfs4_exchange_id_done(struct rpc_task *task, void *data)
cdata->res.server_scope = NULL;
}
}
+out:
cdata->rpc_status = status;
+ return;
+
+session_trunk:
+ if (status == 0)
+ status = nfs4_detect_session_trunking(clp, &cdata->res,
+ cdata->xdata.xprt);
+ goto out;
}

static void nfs4_exchange_id_release(void *data)
@@ -7114,6 +7133,10 @@ static void nfs4_exchange_id_release(void *data)
(struct nfs41_exchange_id_data *)data;

nfs_put_client(cdata->args.client);
+ if (cdata->session_trunk) {
+ xprt_put(cdata->xdata.xprt);
+ xprt_switch_put(cdata->xdata.xps);
+ }
kfree(cdata->res.impl_id);
kfree(cdata->res.server_scope);
kfree(cdata->res.server_owner);
@@ -7131,7 +7154,7 @@ static const struct rpc_call_ops nfs4_exchange_id_call_ops = {
* Wrapper for EXCHANGE_ID operation.
*/
static int _nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred,
- u32 sp4_how)
+ u32 sp4_how, struct nfs41_test_xprt_data *xdata)
{
nfs4_verifier verifier;
struct rpc_message msg = {
@@ -7151,6 +7174,11 @@ static int _nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred,
if (!atomic_inc_not_zero(&clp->cl_count))
goto out;

+ /* Don't test session trunking against the established mount rpc_xprt */
+ if (xdata &&
+ xdata->xprt == rcu_access_pointer(clp->cl_rpcclient->cl_xprt))
+ return 1;
+
status = -ENOMEM;
calldata = kzalloc(sizeof(*calldata), GFP_NOFS);
if (!calldata)
@@ -7196,7 +7224,14 @@ static int _nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred,
status = -EINVAL;
goto out_impl_id;
}
-
+ if (xdata) {
+ calldata->session_trunk = 1;
+ calldata->xdata.xprt = xdata->xprt;
+ calldata->xdata.xps = xdata->xps;
+ task_setup_data.rpc_xprt = xdata->xprt;
+ task_setup_data.flags =
+ RPC_TASK_SOFT|RPC_TASK_SOFTCONN|RPC_TASK_ASYNC;
+ }
calldata->args.verifier = &verifier;
calldata->args.client = clp;
#ifdef CONFIG_NFS_V4_1_MIGRATION
@@ -7217,9 +7252,13 @@ static int _nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred,
goto out_impl_id;
}

- status = rpc_wait_for_completion_task(task);
- if (!status)
+ if (!xdata) {
+ status = rpc_wait_for_completion_task(task);
+ if (!status)
+ status = calldata->rpc_status;
+ } else /* session trunking test */
status = calldata->rpc_status;
+
rpc_put_task(task);
out:
if (clp->cl_implid != NULL)
@@ -7262,13 +7301,59 @@ int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
/* try SP4_MACH_CRED if krb5i/p */
if (authflavor == RPC_AUTH_GSS_KRB5I ||
authflavor == RPC_AUTH_GSS_KRB5P) {
- status = _nfs4_proc_exchange_id(clp, cred, SP4_MACH_CRED);
+ status = _nfs4_proc_exchange_id(clp, cred, SP4_MACH_CRED, NULL);
if (!status)
return 0;
}

/* try SP4_NONE */
- return _nfs4_proc_exchange_id(clp, cred, SP4_NONE);
+ return _nfs4_proc_exchange_id(clp, cred, SP4_NONE, NULL);
+}
+
+/**
+ * nfs4_test_session_trunk
+ * Test the connection with an rpc null ping.
+ * Test for session trunking with an asynchronous exchange_id call.
+ * Upon success, add a new transport
+ * to the rpc_clnt
+ *
+ * @clnt: struct rpc_clnt to get new transport
+ * @xps: the rpc_xprt_switch to hold the new transport
+ * @xprt: the rpc_xprt to test
+ * @data: call data for _nfs4_proc_exchange_id.
+ */
+int nfs4_test_session_trunk(struct rpc_clnt *clnt, struct rpc_xprt_switch *xps,
+ struct rpc_xprt *xprt, void *data)
+{
+ struct nfs4_add_xprt_data *adata = (struct nfs4_add_xprt_data *)data;
+ struct nfs41_test_xprt_data xdata = {
+ .xprt = xprt,
+ .xps = xps,
+ };
+ u32 sp4_how;
+ int status;
+
+ dprintk("--> %s try %s\n", __func__,
+ xprt->address_strings[RPC_DISPLAY_ADDR]);
+
+ xprt = xprt_get(xprt);
+ /* Test the connection */
+ status = rpc_clnt_test_xprt(clnt, xprt);
+ if (status < 0) {
+ dprintk(" %s rpc_clnt_test_xprt failed for %s\n", __func__,
+ xprt->address_strings[RPC_DISPLAY_ADDR]);
+ xprt_put(xprt);
+ goto out;
+ }
+ xps = xprt_switch_get(xps);
+
+ sp4_how = (adata->clp->cl_sp4_flags == 0 ? SP4_NONE : SP4_MACH_CRED);
+
+ /* Test connection for session trunking. Async exchange_id call */
+ status = _nfs4_proc_exchange_id(adata->clp, adata->cred, sp4_how,
+ &xdata);
+out:
+ return status;
}

static int _nfs4_proc_destroy_clientid(struct nfs_client *clp,
diff --git a/net/sunrpc/xprtmultipath.c b/net/sunrpc/xprtmultipath.c
index 66c9d63..505d3b9 100644
--- a/net/sunrpc/xprtmultipath.c
+++ b/net/sunrpc/xprtmultipath.c
@@ -145,6 +145,7 @@ struct rpc_xprt_switch *xprt_switch_get(struct rpc_xprt_switch *xps)
return xps;
return NULL;
}
+EXPORT_SYMBOL_GPL(xprt_switch_get);

/**
* xprt_switch_put - Release a reference to a rpc_xprt_switch
@@ -157,6 +158,7 @@ void xprt_switch_put(struct rpc_xprt_switch *xps)
if (xps != NULL)
kref_put(&xps->xps_kref, xprt_switch_free);
}
+EXPORT_SYMBOL_GPL(xprt_switch_put);

/**
* rpc_xprt_switch_set_roundrobin - Set a round-robin policy on rpc_xprt_switch
--
1.8.3.1


2016-07-27 18:41:28

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 7 8/8] NFS pnfs data server multipath session trunking

From: Andy Adamson <[email protected]>

Try all multipath addresses for a data server. The first address that
successfully connects and creates a session is the DS mount address.
All subsequent addresses are tested for session trunking and
added as aliases.

Signed-off-by: Andy Adamson <[email protected]>
---
fs/nfs/nfs4_fs.h | 2 ++
fs/nfs/nfs4proc.c | 2 ++
fs/nfs/pnfs_nfs.c | 52 ++++++++++++++++++++++++++++++++++++++--------------
3 files changed, 42 insertions(+), 14 deletions(-)

diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index b2a94ca..8e4343f 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -59,6 +59,8 @@ struct nfs4_minor_version_ops {
struct nfs4_lock_state *);
struct nfs_seqid *
(*alloc_seqid)(struct nfs_seqid_counter *, gfp_t);
+ int (*session_trunk)(struct rpc_clnt *, struct rpc_xprt_switch *,
+ struct rpc_xprt *, void *);
const struct rpc_call_ops *call_sync_ops;
const struct nfs4_state_recovery_ops *reboot_recovery_ops;
const struct nfs4_state_recovery_ops *nograce_recovery_ops;
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index fa9ae75..8767720 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -8934,6 +8934,7 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
.find_root_sec = nfs41_find_root_sec,
.free_lock_state = nfs41_free_lock_state,
.alloc_seqid = nfs_alloc_no_seqid,
+ .session_trunk = nfs4_test_session_trunk,
.call_sync_ops = &nfs41_call_sync_ops,
.reboot_recovery_ops = &nfs41_reboot_recovery_ops,
.nograce_recovery_ops = &nfs41_nograce_recovery_ops,
@@ -8963,6 +8964,7 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
.free_lock_state = nfs41_free_lock_state,
.call_sync_ops = &nfs41_call_sync_ops,
.alloc_seqid = nfs_alloc_no_seqid,
+ .session_trunk = nfs4_test_session_trunk,
.reboot_recovery_ops = &nfs41_reboot_recovery_ops,
.nograce_recovery_ops = &nfs41_nograce_recovery_ops,
.state_renewal_ops = &nfs41_state_renewal_ops,
diff --git a/fs/nfs/pnfs_nfs.c b/fs/nfs/pnfs_nfs.c
index f3468b5..7cc1c4d 100644
--- a/fs/nfs/pnfs_nfs.c
+++ b/fs/nfs/pnfs_nfs.c
@@ -690,13 +690,44 @@ static int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv,
dprintk("%s: DS %s: trying address %s\n",
__func__, ds->ds_remotestr, da->da_remotestr);

- clp = nfs4_set_ds_client(mds_srv,
- (struct sockaddr *)&da->da_addr,
- da->da_addrlen, IPPROTO_TCP,
- timeo, retrans, minor_version,
- au_flavor);
- if (!IS_ERR(clp))
- break;
+ if (!IS_ERR(clp) && clp->cl_mvops->session_trunk) {
+ struct xprt_create xprt_args = {
+ .ident = XPRT_TRANSPORT_TCP,
+ .net = clp->cl_net,
+ .dstaddr = (struct sockaddr *)&da->da_addr,
+ .addrlen = da->da_addrlen,
+ .servername = clp->cl_hostname,
+ };
+ struct nfs4_add_xprt_data xprtdata = {
+ .clp = clp,
+ };
+ xprtdata.cred = nfs4_get_clid_cred(clp);
+
+ /**
+ * Test this address for session trunking and
+ * add as an alias
+ */
+ rpc_clnt_add_xprt(clp->cl_rpcclient, &xprt_args,
+ clp->cl_mvops->session_trunk,
+ &xprtdata);
+ } else {
+ clp = nfs4_set_ds_client(mds_srv,
+ (struct sockaddr *)&da->da_addr,
+ da->da_addrlen, IPPROTO_TCP,
+ timeo, retrans, minor_version,
+ au_flavor);
+ if (IS_ERR(clp))
+ continue;
+
+ status = nfs4_init_ds_session(clp,
+ mds_srv->nfs_client->cl_lease_time);
+ if (status) {
+ nfs_put_client(clp);
+ clp = ERR_PTR(-EIO);
+ continue;
+ }
+
+ }
}

if (IS_ERR(clp)) {
@@ -704,18 +735,11 @@ static int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv,
goto out;
}

- status = nfs4_init_ds_session(clp, mds_srv->nfs_client->cl_lease_time);
- if (status)
- goto out_put;
-
smp_wmb();
ds->ds_clp = clp;
dprintk("%s [new] addr: %s\n", __func__, ds->ds_remotestr);
out:
return status;
-out_put:
- nfs_put_client(clp);
- goto out;
}

/*
--
1.8.3.1


2016-07-27 18:41:30

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 7 6/8] SUNRPC add an RPC null call to test session trunking connection

From: Andy Adamson <[email protected]>

Signed-off-by: Andy Adamson <[email protected]>
---
include/linux/sunrpc/clnt.h | 2 ++
net/sunrpc/clnt.c | 18 ++++++++++++++++++
2 files changed, 20 insertions(+)

diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
index 99410bb..ebc83df 100644
--- a/include/linux/sunrpc/clnt.h
+++ b/include/linux/sunrpc/clnt.h
@@ -189,6 +189,8 @@ int rpc_clnt_test_and_add_xprt(struct rpc_clnt *clnt,
struct rpc_xprt_switch *xps,
struct rpc_xprt *xprt,
void *dummy);
+int rpc_clnt_test_xprt(struct rpc_clnt *clnt,
+ struct rpc_xprt *xprt);
int rpc_clnt_add_xprt(struct rpc_clnt *, struct xprt_create *,
int (*setup)(struct rpc_clnt *,
struct rpc_xprt_switch *,
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 08f3e9e..425ff79 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -2615,6 +2615,24 @@ int rpc_clnt_test_and_add_xprt(struct rpc_clnt *clnt,
}
EXPORT_SYMBOL_GPL(rpc_clnt_test_and_add_xprt);

+int rpc_clnt_test_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt)
+{
+ struct rpc_cred *cred;
+ struct rpc_task *task;
+ int status;
+
+ cred = authnull_ops.lookup_cred(NULL, NULL, 0);
+ task = rpc_call_null_helper(clnt, xprt, cred,
+ RPC_TASK_SOFT | RPC_TASK_SOFTCONN, NULL, NULL);
+ put_rpccred(cred);
+ if (IS_ERR(task))
+ return PTR_ERR(task);
+ status = task->tk_status;
+ rpc_put_task(task);
+ return status;
+}
+EXPORT_SYMBOL_GPL(rpc_clnt_test_xprt);
+
/**
* rpc_clnt_add_xprt - Add a new transport to a rpc_clnt
* @clnt: pointer to struct rpc_clnt
--
1.8.3.1