Skip to contentSkip to conversation

nfs41 client patches for review

RFC In review Benny Halevy 2 participants 115 messages Nov 10, 2008 – Nov 17, 2008 Page 2 of 2

Subsystems (4)
lore
Share & tools
lore
#64 lore raw

RFC 43/85 nfs41: use rpc prepare call state for session reset

In reply to Benny Halevy

From: Andy Adamson <[email protected]>

---
 fs/nfs/internal.h |    4 ++++
 fs/nfs/nfs4proc.c |   22 +++++++++++++++++-----
 fs/nfs/read.c     |    2 +-
 fs/nfs/unlink.c   |    2 +-
 fs/nfs/write.c    |    2 +-
 5 files changed, 24 insertions(+), 8 deletions(-)
fs/nfs/internal.h +4 -0
index f78720b..133ad76 100644
@@ -130,6 +130,10 @@ extern __be32 * nfs_decode_dirent(__be32 *, struct nfs_entry *, int);
 extern struct rpc_procinfo nfs3_procedures[];
 extern __be32 *nfs3_decode_dirent(__be32 *, struct nfs_entry *, int);
 
+/* nfs4proc.c */
+extern void nfs4_restart_rpc(struct rpc_task *task,
+			     const struct nfs_server *server);
+
 /* nfs4xdr.c */
 #ifdef CONFIG_NFS_V4
 extern __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus);
fs/nfs/nfs4proc.c +17 -5
index f6107b0..917d630 100644
@@ -593,6 +593,18 @@ static void nfs4_sequence_done(const struct nfs_server *server,
 #endif /* CONFIG_NFS_V4_1 */
 }
 
+void nfs4_restart_rpc(struct rpc_task *task, const struct nfs_server *server)
+{
+#ifdef CONFIG_NFS_V4_1
+	if (server->nfs_client->cl_minorversion &&
+	    nfs41_test_session_reset(server->nfs_client->cl_session)) {
+		rpc_restart_call_prepare(task);
+		return;
+	}
+#endif /* CONFIG_NFS_V4_1 */
+	rpc_restart_call(task);
+}
+
 static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo)
 {
 	struct nfs_inode *nfsi = NFS_I(dir);
@@ -1638,7 +1650,7 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
 			break;
 		default:
 			if (nfs4_async_handle_error(task, server) == -EAGAIN) {
-				rpc_restart_call(task);
+				nfs4_restart_rpc(task, server);
 				return;
 			}
 	}
@@ -2871,7 +2883,7 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data)
 	/* nfs4_sequence_free_slot called in the read rpc_call_done */
 	nfs4_sequence_done(server, &data->res.seq_res, task->tk_status);
 	if (nfs4_async_handle_error(task, server) == -EAGAIN) {
-		rpc_restart_call(task);
+		nfs4_restart_rpc(task, server);
 		dprintk("<-- %s status= %d\n", __func__, -EAGAIN);
 		return -EAGAIN;
 	}
@@ -2899,7 +2911,7 @@ static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data)
 	/* slot is freed in nfs_writeback_done */
 	nfs4_sequence_done(server, &data->res.seq_res, task->tk_status);
 	if (nfs4_async_handle_error(task, server) == -EAGAIN) {
-		rpc_restart_call(task);
+		nfs4_restart_rpc(task, server);
 		return -EAGAIN;
 	}
 	if (task->tk_status >= 0) {
@@ -2927,7 +2939,7 @@ static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data)
 
 	nfs4_sequence_done(server, &data->res.seq_res, task->tk_status);
 	if (nfs4_async_handle_error(task, server) == -EAGAIN) {
-		rpc_restart_call(task);
+		nfs4_restart_rpc(task, server);
 		return -EAGAIN;
 	}
 	nfs4_sequence_free_slot(server, &data->res.seq_res);
@@ -3701,7 +3713,7 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)
 			break;
 		default:
 			if (nfs4_async_handle_error(task, server) == -EAGAIN)
-				rpc_restart_call(task);
+				nfs4_restart_rpc(task, server);
 	}
 	nfs4_sequence_free_slot(server, &calldata->res.seq_res);
 }
fs/nfs/read.c +1 -1
index 3c77b26..5b8db19 100644
@@ -367,7 +367,7 @@ static void nfs_readpage_retry(struct rpc_task *task, struct nfs_read_data *data
 	argp->offset += resp->count;
 	argp->pgbase += resp->count;
 	argp->count -= resp->count;
-	rpc_restart_call(task);
+	nfs4_restart_rpc(task, NFS_SERVER(data->inode));
 	return;
 out:
 	nfs4_sequence_free_slot(NFS_SERVER(data->inode), &data->res.seq_res);
fs/nfs/unlink.c +1 -1
index 3df3794..2ac5dd5 100644
@@ -83,7 +83,7 @@ static void nfs_async_unlink_done(struct rpc_task *task, void *calldata)
 	struct inode *dir = data->dir;
 
 	if (!NFS_PROTO(dir)->unlink_done(task, dir))
-		rpc_restart_call(task);
+		nfs4_restart_rpc(task, NFS_SERVER(dir));
 }
 
 /**
fs/nfs/write.c +1 -1
index 7d3fdc8..86573f7 100644
@@ -1179,7 +1179,7 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
 				 */
 				argp->stable = NFS_FILE_SYNC;
 			}
-			rpc_restart_call(task);
+			nfs4_restart_rpc(task, NFS_SERVER(data->inode));
 			return -EAGAIN;
 		}
 		if (time_before(complain, jiffies)) {
Signature · 1 line
1.6.0.2
#65 lore raw

RE: [pnfs] [RFC 43/85] nfs41: use rpc prepare call state for sessionreset

http://linux-nfs.org/pipermail/pnfs/2008-November/005264.html
from Andy should probably be squashed into this patch.

Benny

________________________________

From: [email protected] on behalf of Benny Halevy
Sent: Mon 2008-11-10 22:25
To: [email protected]
Cc: [email protected]; [email protected]
Subject: [pnfs] [RFC 43/85] nfs41: use rpc prepare call state for sessionreset

From: Andy Adamson <[email protected]>

---
 fs/nfs/internal.h |    4 ++++
 fs/nfs/nfs4proc.c |   22 +++++++++++++++++-----
 fs/nfs/read.c     |    2 +-
 fs/nfs/unlink.c   |    2 +-
 fs/nfs/write.c    |    2 +-
 5 files changed, 24 insertions(+), 8 deletions(-)
fs/nfs/internal.h +4 -0
index f78720b..133ad76 100644
@@ -130,6 +130,10 @@ extern __be32 * nfs_decode_dirent(__be32 *, struct nfs_entry *, int);
 extern struct rpc_procinfo nfs3_procedures[];
 extern __be32 *nfs3_decode_dirent(__be32 *, struct nfs_entry *, int);
 
+/* nfs4proc.c */
+extern void nfs4_restart_rpc(struct rpc_task *task,
+                            const struct nfs_server *server);
+
 /* nfs4xdr.c */
 #ifdef CONFIG_NFS_V4
 extern __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus);
fs/nfs/nfs4proc.c +17 -5
index f6107b0..917d630 100644
@@ -593,6 +593,18 @@ static void nfs4_sequence_done(const struct nfs_server *server,
 #endif /* CONFIG_NFS_V4_1 */
 }
 
+void nfs4_restart_rpc(struct rpc_task *task, const struct nfs_server *server)
+{
+#ifdef CONFIG_NFS_V4_1
+       if (server->nfs_client->cl_minorversion &&
+           nfs41_test_session_reset(server->nfs_client->cl_session)) {
+               rpc_restart_call_prepare(task);
+               return;
+       }
+#endif /* CONFIG_NFS_V4_1 */
+       rpc_restart_call(task);
+}
+
 static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo)
 {
        struct nfs_inode *nfsi = NFS_I(dir);
@@ -1638,7 +1650,7 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
                        break;
                default:
                        if (nfs4_async_handle_error(task, server) == -EAGAIN) {
-                               rpc_restart_call(task);
+                               nfs4_restart_rpc(task, server);
                                return;
                        }
        }
@@ -2871,7 +2883,7 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data)
        /* nfs4_sequence_free_slot called in the read rpc_call_done */
        nfs4_sequence_done(server, &data->res.seq_res, task->tk_status);
        if (nfs4_async_handle_error(task, server) == -EAGAIN) {
-               rpc_restart_call(task);
+               nfs4_restart_rpc(task, server);
                dprintk("<-- %s status= %d\n", __func__, -EAGAIN);
                return -EAGAIN;
        }
@@ -2899,7 +2911,7 @@ static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data)
        /* slot is freed in nfs_writeback_done */
        nfs4_sequence_done(server, &data->res.seq_res, task->tk_status);
        if (nfs4_async_handle_error(task, server) == -EAGAIN) {
-               rpc_restart_call(task);
+               nfs4_restart_rpc(task, server);
                return -EAGAIN;
        }
        if (task->tk_status >= 0) {
@@ -2927,7 +2939,7 @@ static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data)
 
        nfs4_sequence_done(server, &data->res.seq_res, task->tk_status);
        if (nfs4_async_handle_error(task, server) == -EAGAIN) {
-               rpc_restart_call(task);
+               nfs4_restart_rpc(task, server);
                return -EAGAIN;
        }
        nfs4_sequence_free_slot(server, &data->res.seq_res);
@@ -3701,7 +3713,7 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)
                        break;
                default:
                        if (nfs4_async_handle_error(task, server) == -EAGAIN)
-                               rpc_restart_call(task);
+                               nfs4_restart_rpc(task, server);
        }
        nfs4_sequence_free_slot(server, &calldata->res.seq_res);
 }
fs/nfs/read.c +1 -1
index 3c77b26..5b8db19 100644
@@ -367,7 +367,7 @@ static void nfs_readpage_retry(struct rpc_task *task, struct nfs_read_data *data
        argp->offset += resp->count;
        argp->pgbase += resp->count;
        argp->count -= resp->count;
-       rpc_restart_call(task);
+       nfs4_restart_rpc(task, NFS_SERVER(data->inode));
        return;
 out:
        nfs4_sequence_free_slot(NFS_SERVER(data->inode), &data->res.seq_res);
fs/nfs/unlink.c +1 -1
index 3df3794..2ac5dd5 100644
@@ -83,7 +83,7 @@ static void nfs_async_unlink_done(struct rpc_task *task, void *calldata)
        struct inode *dir = data->dir;
 
        if (!NFS_PROTO(dir)->unlink_done(task, dir))
-               rpc_restart_call(task);
+               nfs4_restart_rpc(task, NFS_SERVER(dir));
 }
 
 /**
fs/nfs/write.c +1 -1
index 7d3fdc8..86573f7 100644
@@ -1179,7 +1179,7 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
                                 */
                                argp->stable = NFS_FILE_SYNC;
                        }
-                       rpc_restart_call(task);
+                       nfs4_restart_rpc(task, NFS_SERVER(data->inode));
                        return -EAGAIN;
                }
                if (time_before(complain, jiffies)) {

--
1.6.0.2

_______________________________________________
pNFS mailing list
[email protected]
http://linux-nfs.org/cgi-bin/mailman/listinfo/pnfs

#66 lore raw

RFC 44/85 nfs41: kick start nfs41 session recovery when handling errors

In reply to Benny Halevy

From: Andy Adamson <[email protected]>

---
 fs/nfs/nfs4proc.c |   71 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 71 insertions(+), 0 deletions(-)
fs/nfs/nfs4proc.c +71 -0
index 917d630..a4f5b15 100644
@@ -3256,6 +3256,25 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server)
 				rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
 			task->tk_status = 0;
 			return -EAGAIN;
+#if defined(CONFIG_NFS_V4_1)
+		case -NFS4ERR_BADSESSION:
+		case -NFS4ERR_BADSLOT:
+		case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
+		case -NFS4ERR_BACK_CHAN_BUSY:
+		case -NFS4ERR_SEQ_MISORDERED:
+		case -NFS4ERR_SEQUENCE_POS:
+		case -NFS4ERR_REQ_TOO_BIG:
+		case -NFS4ERR_REP_TOO_BIG:
+		case -NFS4ERR_REP_TOO_BIG_TO_CACHE:
+		case -NFS4ERR_RETRY_UNCACHED_REP:
+		case -NFS4ERR_TOO_MANY_OPS:
+		case -NFS4ERR_OP_NOT_IN_SESSION:
+			dprintk("%s ERROR %d, Reset session\n", __func__,
+				task->tk_status);
+			nfs41_set_session_reset(clp->cl_session);
+			task->tk_status = 0;
+			return -EAGAIN;
+#endif /* CONFIG_NFS_V4_1 */
 		case -NFS4ERR_DELAY:
 			nfs_inc_server_stats(server, NFSIOS_DELAY);
 		case -NFS4ERR_GRACE:
@@ -3331,7 +3350,30 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc
 			ret = nfs4_wait_clnt_recover(server->client, clp);
 			if (ret == 0)
 				exception->retry = 1;
+#if !defined(CONFIG_NFS_V4_1)
 			break;
+#else /* !defined(CONFIG_NFS_V4_1) */
+			if (!server->nfs_client->cl_minorversion)
+				break;
+			/* FALLTHROUGH */
+		case -NFS4ERR_BADSESSION:
+		case -NFS4ERR_BADSLOT:
+		case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
+		case -NFS4ERR_BACK_CHAN_BUSY:
+		case -NFS4ERR_SEQ_MISORDERED:
+		case -NFS4ERR_SEQUENCE_POS:
+		case -NFS4ERR_REQ_TOO_BIG:
+		case -NFS4ERR_REP_TOO_BIG:
+		case -NFS4ERR_REP_TOO_BIG_TO_CACHE:
+		case -NFS4ERR_RETRY_UNCACHED_REP:
+		case -NFS4ERR_TOO_MANY_OPS:
+		case -NFS4ERR_OP_NOT_IN_SESSION:
+			dprintk("%s ERROR: %d Reset session\n", __func__,
+				errorcode);
+			nfs41_set_session_reset(clp->cl_session);
+			exception->retry = 1;
+			/* FALLTHROUGH */
+#endif /* !defined(CONFIG_NFS_V4_1) */
 		case -NFS4ERR_FILE_OPEN:
 		case -NFS4ERR_GRACE:
 		case -NFS4ERR_DELAY:
@@ -4735,8 +4777,37 @@ static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred)
 void nfs41_sequence_call_done(struct rpc_task *task, void *data)
 {
 	struct nfs_server *server = (struct nfs_server *)data;
+	struct nfs_client *clp = server->nfs_client;
 
 	nfs4_sequence_done(server, task->tk_msg.rpc_resp, task->tk_status);
+
+	if (task->tk_status < 0) {
+		switch (task->tk_status) {
+		case -NFS4ERR_STALE_CLIENTID:
+		case -NFS4ERR_EXPIRED:
+		case -NFS4ERR_CB_PATH_DOWN:
+			nfs4_schedule_state_recovery(clp);
+		case -NFS4ERR_BADSLOT:
+		case -NFS4ERR_BADSESSION:
+		case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
+		case -NFS4ERR_BACK_CHAN_BUSY:
+		case -NFS4ERR_SEQ_MISORDERED:
+		case -NFS4ERR_SEQUENCE_POS:
+		case -NFS4ERR_REQ_TOO_BIG:
+		case -NFS4ERR_REP_TOO_BIG:
+		case -NFS4ERR_REP_TOO_BIG_TO_CACHE:
+		case -NFS4ERR_RETRY_UNCACHED_REP:
+		case -NFS4ERR_TOO_MANY_OPS:
+		case -NFS4ERR_OP_NOT_IN_SESSION:
+			dprintk("%s ERROR %d Reset session\n", __func__,
+				task->tk_status);
+
+			if (nfs4_async_handle_error(task, server) == -EAGAIN) {
+				nfs4_restart_rpc(task, server);
+				return;
+			}
+		}
+	}
 	nfs4_sequence_free_slot(server, task->tk_msg.rpc_resp);
 	dprintk("%s rpc_cred %p\n", __func__, task->tk_msg.rpc_cred);
 
Signature · 1 line
1.6.0.2
#67 lore raw

RFC 45/85 nfs41: start session recovery from nfs4_setup_sequence

In reply to Benny Halevy
---
 fs/nfs/nfs4proc.c |    7 +++++--
 1 files changed, 5 insertions(+), 2 deletions(-)
fs/nfs/nfs4proc.c +5 -2
index a4f5b15..12f3b9f 100644
@@ -491,8 +491,11 @@ int nfs4_setup_sequence(struct nfs_client *clp,
 	if (clp->cl_minorversion == 0 || res->sr_slot)
 		goto out;
 	BUG_ON(clp->cl_minorversion != 1);
-	ret = nfs41_setup_sequence(clp->cl_session, args, res,
-				   cache_reply, task);
+	if (nfs41_test_session_alloc(clp->cl_session))
+		ret = nfs41_recover_session_sync(clp->cl_session);
+	if (!ret)
+		ret = nfs41_setup_sequence(clp->cl_session, args, res,
+					   cache_reply, task);
 	if (ret) {
 		memset(res, 0, sizeof(*res));
 		if (ret != -EAGAIN) {
Signature · 1 line
1.6.0.2
#68 lore raw

RFC 46/85 nfs41: introduce get_state_renewal_cred

In reply to Benny Halevy

From: Andy Adamson <[email protected]>

Reuse the cred used at exchange_id time for
sending SEQUENCE to renew the client's lease.

---
 fs/nfs/client.c           |    4 ++++
 fs/nfs/nfs4_fs.h          |    4 ++++
 fs/nfs/nfs4proc.c         |   13 +++++++++++++
 fs/nfs/nfs4renewd.c       |    2 +-
 fs/nfs/nfs4state.c        |   14 +++++++++++++-
 include/linux/nfs_fs_sb.h |    1 +
 6 files changed, 36 insertions(+), 2 deletions(-)
fs/nfs/client.c +4 -0
index 560b10b..c69424d 100644
@@ -207,6 +207,10 @@ static void nfs_free_client(struct nfs_client *clp)
 
 	if (clp->cl_machine_cred != NULL)
 		put_rpccred(clp->cl_machine_cred);
+#ifdef CONFIG_NFS_V4_1
+	if (clp->cl_ex_cred != NULL)
+		put_rpccred(clp->cl_ex_cred);
+#endif /* CONFIG_NFS_V4_1 */
 
 	kfree(clp->cl_hostname);
 	kfree(clp);
fs/nfs/nfs4_fs.h +4 -0
index ff367df..904b19e 100644
@@ -167,6 +167,7 @@ struct nfs4_state_recovery_ops {
 
 struct nfs4_state_maintenance_ops {
 	int (*sched_state_renewal)(struct nfs_client *, struct rpc_cred *);
+	struct rpc_cred * (*get_state_renewal_cred)(struct nfs_client *);
 };
 
 extern struct dentry_operations nfs4_dentry_operations;
@@ -228,6 +229,9 @@ extern void nfs4_renew_state(struct work_struct *);
 
 /* nfs4state.c */
 struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp);
+#ifdef CONFIG_NFS_V4_1
+struct rpc_cred *nfs41_get_state_renewal_cred(struct nfs_client *clp);
+#endif /* CONFIG_NFS_V4_1 */
 
 extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *);
 extern void nfs4_put_state_owner(struct nfs4_state_owner *);
fs/nfs/nfs4proc.c +13 -0
index 12f3b9f..5b1f6d0 100644
@@ -4317,6 +4317,17 @@ static int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
 		else if (++clp->cl_id_uniquifier == 0)
 			break;
 	}
+	if (status == 0) {
+		if (clp->cl_ex_cred) {
+			dprintk("%s put cl_ex_cred %p\n", __func__,
+				clp->cl_ex_cred);
+			put_rpccred(clp->cl_ex_cred);
+		}
+
+		BUG_ON(cred == NULL);
+		clp->cl_ex_cred = get_rpccred(cred);
+		dprintk("%s set cl_ex_cred %p\n", __func__, clp->cl_ex_cred);
+	}
 
 	dprintk("<-- %s status= %d\n", __func__, status);
 	return status;
@@ -4907,11 +4918,13 @@ struct nfs4_state_recovery_ops nfs41_network_partition_recovery_ops = {
 
 struct nfs4_state_maintenance_ops nfs40_state_renewal_ops = {
 	.sched_state_renewal = nfs4_proc_async_renew,
+	.get_state_renewal_cred = nfs4_get_renew_cred,
 };
 
 #if defined(CONFIG_NFS_V4_1)
 struct nfs4_state_maintenance_ops nfs41_state_renewal_ops = {
 	.sched_state_renewal = nfs41_proc_async_sequence,
+	.get_state_renewal_cred = nfs41_get_state_renewal_cred,
 };
 #endif
 
fs/nfs/nfs4renewd.c +1 -1
index a1bab0c..1d609ed 100644
@@ -79,7 +79,7 @@ nfs4_renew_state(struct work_struct *work)
 	timeout = (2 * lease) / 3 + (long)last - (long)now;
 	/* Are we close to a lease timeout? */
 	if (time_after(now, last + lease/3)) {
-		cred = nfs4_get_renew_cred(clp);
+		cred = ops->get_state_renewal_cred(clp);
 		if (cred == NULL) {
 			set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
 			spin_unlock(&clp->cl_lock);
fs/nfs/nfs4state.c +13 -1
index cf6666d..5a91072 100644
@@ -73,6 +73,16 @@ static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred)
 	return status;
 }
 
+#if defined(CONFIG_NFS_V4_1)
+struct rpc_cred *
+nfs41_get_state_renewal_cred(struct nfs_client *clp)
+{
+	if (clp->cl_ex_cred)
+		get_rpccred(clp->cl_ex_cred);
+	return (clp->cl_ex_cred);
+}
+#endif /* CONFIG_NFS_V4_1 */
+
 static struct rpc_cred *nfs4_get_machine_cred(struct nfs_client *clp)
 {
 	struct rpc_cred *cred = NULL;
@@ -943,6 +953,7 @@ static int reclaimer(void *ptr)
 	struct nfs4_state_owner *sp;
 	struct rb_node *pos;
 	struct nfs4_state_recovery_ops *ops;
+	struct nfs4_state_maintenance_ops *m_ops;
 	struct rpc_cred *cred;
 	int status = 0;
 
@@ -957,8 +968,9 @@ static int reclaimer(void *ptr)
 restart_loop:
 	dprintk("%s: starting loop\n", __func__);
 	ops = nfs4_network_partition_recovery_ops[clp->cl_minorversion];
+	m_ops = nfs4_state_renewal_ops[clp->cl_minorversion];
 	/* Are there any open files on this volume? */
-	cred = nfs4_get_renew_cred(clp);
+	cred = m_ops->get_state_renewal_cred(clp);
 	if (cred != NULL) {
 		/* Yes there are: try to renew the old lease */
 		status = ops->renew_lease(clp, cred);
include/linux/nfs_fs_sb.h +1 -0
index 9917c09..17720b9 100644
@@ -81,6 +81,7 @@ struct nfs_client {
 	u32			cl_seqid;
 	/* The flags used for obtaining the clientid during EXCHANGE_ID */
 	u32			cl_exchange_flags;
+	struct rpc_cred		*cl_ex_cred;	/* exchange_id credential */
 	struct nfs4_session	*cl_session; 	/* sharred session */
 #endif /* CONFIG_NFS_V4_1 */
 };
Signature · 1 line
1.6.0.2
#69 lore raw

RFC 47/85 nfs41: get cred in exchange_id when cred arg is NULL

In reply to Benny Halevy

When creating a session to a pnfs data server, the cred is passed down as NULL. Either use the saved cl_ex_cred or get a new one using nfs4_get_setclientid_cred.

---
 fs/nfs/nfs4_fs.h   |    1 +
 fs/nfs/nfs4proc.c  |   18 +++++++++++++++---
 fs/nfs/nfs4state.c |    2 +-
 3 files changed, 17 insertions(+), 4 deletions(-)
fs/nfs/nfs4_fs.h +1 -0
index 904b19e..b9771f2 100644
@@ -229,6 +229,7 @@ extern void nfs4_renew_state(struct work_struct *);
 
 /* nfs4state.c */
 struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp);
+struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp);
 #ifdef CONFIG_NFS_V4_1
 struct rpc_cred *nfs41_get_state_renewal_cred(struct nfs_client *clp);
 #endif /* CONFIG_NFS_V4_1 */
fs/nfs/nfs4proc.c +15 -3
index 5b1f6d0..8c28d0c 100644
@@ -4276,6 +4276,7 @@ static int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
 	};
 	int status;
 	int loop = 0;
+	int got_cred = 0;
 	struct rpc_message msg = {
 		.rpc_proc = nfs4_proc(clp, NFSPROC4_CLNT_EXCHANGE_ID),
 		.rpc_argp = &args,
@@ -4286,6 +4287,16 @@ static int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
 
 	dprintk("--> %s\n", __func__);
 	BUG_ON(clp == NULL);
+
+	if (!cred) {
+		/* FIXME: do we need to lock clp and take a ref count on cl_ex_cred? */
+		cred = clp->cl_ex_cred;
+		if (!cred) {
+			cred = nfs4_get_setclientid_cred(clp);
+			got_cred = (cred != NULL);
+		}
+	}
+
 	p = (u32 *)verifier.data;
 	*p++ = htonl((u32)clp->cl_boot_time.tv_sec);
 	*p = htonl((u32)clp->cl_boot_time.tv_nsec);
@@ -4317,17 +4328,18 @@ static int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
 		else if (++clp->cl_id_uniquifier == 0)
 			break;
 	}
-	if (status == 0) {
+	if ((status == 0) && (cred != clp->cl_ex_cred) && (cred != NULL)) {
+		/* FIXME: do we need to lock clp here? */
 		if (clp->cl_ex_cred) {
 			dprintk("%s put cl_ex_cred %p\n", __func__,
 				clp->cl_ex_cred);
 			put_rpccred(clp->cl_ex_cred);
 		}
-
-		BUG_ON(cred == NULL);
 		clp->cl_ex_cred = get_rpccred(cred);
 		dprintk("%s set cl_ex_cred %p\n", __func__, clp->cl_ex_cred);
 	}
+	if (got_cred)
+		put_rpccred(cred);
 
 	dprintk("<-- %s status= %d\n", __func__, status);
 	return status;
fs/nfs/nfs4state.c +1 -1
index 5a91072..693e5a6 100644
@@ -122,7 +122,7 @@ struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp)
 	return cred;
 }
 
-static struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp)
+struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp)
 {
 	struct nfs4_state_owner *sp;
 	struct rb_node *pos;
Signature · 1 line
1.6.0.2
#70 lore raw

RFC 48/85 nfs41: establish sessions-based clientid

In reply to Benny Halevy

nfsv4.1 clientid is established via EXCHANGE_ID rather than SETCLIENTID{,_CONFIRM}

This is implemented using a new establish_clid method in nfs4_state_recovery_ops.

nfs41: establish clientid via exchange id only if cred != NULL

Quoted 1 line

From 2.6.26 reclaimer() uses machine cred for setting up the client id

therefore it is never expected to be NULL.

---
 fs/nfs/nfs4_fs.h   |    2 ++
 fs/nfs/nfs4proc.c  |    4 ++++
 fs/nfs/nfs4state.c |    5 +++--
 3 files changed, 9 insertions(+), 2 deletions(-)
fs/nfs/nfs4_fs.h +2 -0
index b9771f2..44282b0 100644
@@ -163,6 +163,7 @@ struct nfs4_state_recovery_ops {
 	int (*recover_open)(struct nfs4_state_owner *, struct nfs4_state *);
 	int (*recover_lock)(struct nfs4_state *, struct file_lock *);
 	int (*renew_lease)(struct nfs_client *, struct rpc_cred *);
+	int (*establish_clid)(struct nfs_client *, struct rpc_cred *);
 };
 
 struct nfs4_state_maintenance_ops {
@@ -186,6 +187,7 @@ extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct rpc_cred *)
 extern int nfs4_proc_async_renew(struct nfs_client *, struct rpc_cred *);
 extern int nfs4_proc_renew(struct nfs_client *, struct rpc_cred *);
 extern int nfs4_wait_bit_killable(void *);
+extern int nfs4_init_clientid(struct nfs_client *, struct rpc_cred *);
 extern int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait);
 extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *);
 extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *);
fs/nfs/nfs4proc.c +4 -0
index 8c28d0c..749027d 100644
@@ -4904,6 +4904,7 @@ struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = {
 	.recover_open	= nfs4_open_reclaim,
 	.recover_lock	= nfs4_lock_reclaim,
 	.renew_lease	= nfs4_proc_renew,
+	.establish_clid = nfs4_init_clientid,
 };
 
 #if defined(CONFIG_NFS_V4_1)
@@ -4911,6 +4912,7 @@ struct nfs4_state_recovery_ops nfs41_reboot_recovery_ops = {
 	.recover_open	= nfs4_open_reclaim,
 	.recover_lock	= nfs4_lock_reclaim,
 	.renew_lease	= nfs4_proc_sequence,
+	.establish_clid = nfs4_proc_exchange_id,
 };
 #endif /* CONFIG_NFS_V4_1 */
 
@@ -4918,6 +4920,7 @@ struct nfs4_state_recovery_ops nfs40_network_partition_recovery_ops = {
 	.recover_open	= nfs4_open_expired,
 	.recover_lock	= nfs4_lock_expired,
 	.renew_lease	= nfs4_proc_renew,
+	.establish_clid = nfs4_init_clientid,
 };
 
 #if defined(CONFIG_NFS_V4_1)
@@ -4925,6 +4928,7 @@ struct nfs4_state_recovery_ops nfs41_network_partition_recovery_ops = {
 	.recover_open	= nfs4_open_expired,
 	.recover_lock	= nfs4_lock_expired,
 	.renew_lease	= nfs4_proc_sequence,
+	.establish_clid = nfs4_proc_exchange_id,
 };
 #endif /* CONFIG_NFS_V4_1 */
 
fs/nfs/nfs4state.c +3 -2
index 693e5a6..d3c5e92 100644
@@ -62,7 +62,7 @@ const nfs4_stateid zero_stateid;
 
 static LIST_HEAD(nfs4_clientid_list);
 
-static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred)
+int nfs4_init_clientid(struct nfs_client *clp, struct rpc_cred *cred)
 {
 	int status = nfs4_proc_setclientid(clp, NFS4_CALLBACK,
 			nfs_callback_tcpport, cred);
@@ -991,8 +991,9 @@ restart_loop:
 	nfs4_state_mark_reclaim(clp);
 	status = -ENOENT;
 	cred = nfs4_get_setclientid_cred(clp);
+	dprintk("%s: setclientid_cred %p\n", __func__, cred);
 	if (cred != NULL) {
-		status = nfs4_init_client(clp, cred);
+		status = ops->establish_clid(clp, cred);
 		put_rpccred(cred);
 		/* Handle case where the user hasn't set up machine creds */
 		if (status == -EACCES && cred == clp->cl_machine_cred) {
Signature · 1 line
1.6.0.2
#71 lore raw

RFC 49/85 nfs41: recover lease in _nfs4_lookup_root

In reply to Benny Halevy

This creates the nfsv4.1 session on mount.

---
 fs/nfs/nfs4proc.c |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)
fs/nfs/nfs4proc.c +4 -0
index 749027d..179ea6c 100644
@@ -1956,7 +1956,11 @@ static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
 
 	dprintk("--> %s\n", __func__);
 	nfs_fattr_init(info->fattr);
+	status = nfs4_recover_expired_lease(server);
+	if (status != 0)
+		goto out;
 	status = nfs4_call_sync(server, &msg, &args, &res, 0);
+out:
 	dprintk("<-- %s status= %d\n", __func__, status);
 	return status;
 }
Signature · 1 line
1.6.0.2
#72 lore raw

Re: [pnfs] [RFC 49/85] nfs41: recover lease in _nfs4_lookup_root

Quoted 12 lines · Benny Halevy

This creates the nfsv4.1 session on mount.

---
 fs/nfs/nfs4proc.c |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)
fs/nfs/nfs4proc.c +0 -0
index 749027d..179ea6c 100644

fs/nfs/nfs4proc.c static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle, · line 1961

Quoted 6 lines
fs/nfs/nfs4proc.c +3 -0
@@ -1956,7 +1956,11 @@ static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
 
 	dprintk("--> %s\n", __func__);
 	nfs_fattr_init(info->fattr);
+	status = nfs4_recover_expired_lease(server);
+	if (status != 0)
+		goto out;

review 11-14: do that *only* for minorversion != 0

Quoted 5 lines
fs/nfs/nfs4proc.c +1 -0
 	status = nfs4_call_sync(server, &msg, &args, &res, 0);
+out:
 	dprintk("<-- %s status= %d\n", __func__, status);
 	return status;
 }
#73 lore raw

RFC 50/85 nfs41: schedule state recovery on BAD or DEAD session

In reply to Benny Halevy

From: Andy Adamson <[email protected]>

When a clientid is stale, the sequence operation will return NFS4ERR_BADSESSION or NFS4ERR_DEADSESSION. This will put the session into reset mode, and run the session_reclaimer thread which calls destroy_session and create_session. Destroy_session will also return NFS4ERR_BADSESSION or NFS4ERR_DEADSESSION which is ignored by the session reset. Create_session will return NFS4ERR_STALE_CLIENTID and the session reclaimer sets the nfs_client cl_state to NFS4CLNT_LEASE_EXPIRED.

Catch the NFS4CLNT_LEASE_EXPIRED cl_state in nfs4_find_slot, and attempt to recover the expired client id.

---
 fs/nfs/nfs4proc.c |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)
fs/nfs/nfs4proc.c +7 -0
index 179ea6c..699bbe7 100644
@@ -403,6 +403,13 @@ nfs4_find_slot(struct nfs4_slot_table *tbl, struct rpc_task *task,
 			dprintk("%s Slot Table Empty\n", __func__);
 			spin_unlock(&tbl->slot_tbl_lock);
 			ret = nfs41_recover_session_sync(session);
+			if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED,
+						&session->clp->cl_state)) {
+
+				printk(KERN_WARNING "%s Clientid Reset\n",
+					__func__);
+				nfs4_schedule_state_recovery(session->clp);
+			}
 			if (ret)
 				return slot;
 			nfs41_wait_session_reset(session);
Signature · 1 line
1.6.0.2
#74 lore raw

RFC 51/85 nfs41: state reclaimer renew lease error handling

In reply to Benny Halevy

From: Andy Adamson <[email protected]>

The state reclaimer has been called due to a NFS4_STALE_CLIENTID or NFS4_EXPIRE error. First it tries to renew the old lease (network partion case) which for v4.1 means an nfs4_proc_sequence call. Any error from the sequence operation means that the lease cannot be renewed.

Switch on the minorversion to handle the error return from the renew lease call.

---
 fs/nfs/nfs4state.c |   20 +++++++++++++-------
 1 files changed, 13 insertions(+), 7 deletions(-)
fs/nfs/nfs4state.c +13 -7
index d3c5e92..0d6b902 100644
@@ -975,18 +975,24 @@ restart_loop:
 		/* Yes there are: try to renew the old lease */
 		status = ops->renew_lease(clp, cred);
 		put_rpccred(cred);
-		switch (status) {
-		case 0:
-		case -NFS4ERR_CB_PATH_DOWN:
+		if (!status)
 			goto out;
-		case -NFS4ERR_STALE_CLIENTID:
-		case -NFS4ERR_LEASE_MOVED:
-			ops = nfs4_reboot_recovery_ops[clp->cl_minorversion];
-		}
+		if (clp->cl_minorversion == 0)
+			switch (status) {
+			case -NFS4ERR_CB_PATH_DOWN:
+				goto out;
+			case -NFS4ERR_STALE_CLIENTID:
+			case -NFS4ERR_LEASE_MOVED:
+				break;
+			default:
+				goto establish_clid;
+			}
+		ops = nfs4_reboot_recovery_ops[clp->cl_minorversion];
 	} else {
 		/* "reboot" to ensure we clear all state on the server */
 		clp->cl_boot_time = CURRENT_TIME;
 	}
+establish_clid:
 	/* We're going to have to re-establish a clientid */
 	nfs4_state_mark_reclaim(clp);
 	status = -ENOENT;
Signature · 1 line
1.6.0.2
#75 lore raw

RFC 52/85 nfs41: increment_{open,lock}_seqid

In reply to Benny Halevy

Unlike minorversion0, in nfsv4.1 the open and lock seqids need not be incremented by the client and should always be set to zero.

This is implemented using a new nfs_rpc_ops methods - increment_open_seqid and increment_lock_seqid

---
 fs/nfs/nfs4proc.c  |    4 ++++
 fs/nfs/nfs4state.c |   14 +++++++++-----
 2 files changed, 13 insertions(+), 5 deletions(-)
fs/nfs/nfs4proc.c +4 -0
index 699bbe7..28c3cf8 100644
@@ -1757,6 +1757,10 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait)
 	calldata->state = state;
 	calldata->arg.fh = NFS_FH(state->inode);
 	calldata->arg.stateid = &state->open_stateid;
+	if (server->minorversion) {
+		BUG_ON(server->minorversion != 1);
+		memset(calldata->arg.stateid->data, 0, 4);    /* clear seqid */
+	}
 	/* Serialization for the sequence id */
 	calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid);
 	if (calldata->arg.seqid == NULL)
fs/nfs/nfs4state.c +9 -5
index 0d6b902..6035634 100644
@@ -747,12 +747,16 @@ static void nfs_increment_seqid(int status, struct nfs_seqid *seqid)
 
 void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid)
 {
-	if (status == -NFS4ERR_BAD_SEQID) {
-		struct nfs4_state_owner *sp = container_of(seqid->sequence,
-				struct nfs4_state_owner, so_seqid);
+	struct nfs4_state_owner *sp = container_of(seqid->sequence,
+					struct nfs4_state_owner, so_seqid);
+	struct nfs_server *server = sp->so_server;
+
+	if (status == -NFS4ERR_BAD_SEQID)
 		nfs4_drop_state_owner(sp);
-	}
-	nfs_increment_seqid(status, seqid);
+	if (server->minorversion == 0)
+		nfs_increment_seqid(status, seqid);
+	else
+		BUG_ON(server->minorversion != 1);
 }
 
 /*
Signature · 1 line
1.6.0.2
#76 lore raw

RFC 53/85 nfs41: Add ability to read RPC call direction on TCP stream.

In reply to Benny Halevy

NFSv4.1 callbacks can arrive over an existing connection. This patch adds the logic to read the RPC call direction (call or reply). It does this by updating the state machine to look for the call direction invoking xs_tcp_read_calldir(...) after reading the XID.

To avoid confusion, keep tcp_calldir in code in host order.

---
 net/sunrpc/xprtsock.c |   49 +++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 45 insertions(+), 4 deletions(-)
net/sunrpc/xprtsock.c +45 -4
index 0a50361..1ce05d7 100644
@@ -222,7 +222,8 @@ struct sock_xprt {
 	 * State of TCP reply receive
 	 */
 	__be32			tcp_fraghdr,
-				tcp_xid;
+				tcp_xid,
+				tcp_calldir;	/* Is this a call or a reply? */
 
 	u32			tcp_offset,
 				tcp_reclen;
@@ -259,6 +260,7 @@ struct sock_xprt {
 #define TCP_RCV_COPY_FRAGHDR	(1UL << 1)
 #define TCP_RCV_COPY_XID	(1UL << 2)
 #define TCP_RCV_COPY_DATA	(1UL << 3)
+#define TCP_RCV_COPY_CALLDIR	(1UL << 4)
 
 static inline struct sockaddr *xs_addr(struct rpc_xprt *xprt)
 {
@@ -919,7 +921,7 @@ static inline void xs_tcp_read_fraghdr(struct rpc_xprt *xprt, struct xdr_skb_rea
 	transport->tcp_offset = 0;
 
 	/* Sanity check of the record length */
-	if (unlikely(transport->tcp_reclen < 4)) {
+	if (unlikely(transport->tcp_reclen < 8)) {
 		dprintk("RPC:       invalid TCP record fragment length\n");
 		xprt_force_disconnect(xprt);
 		return;
@@ -954,13 +956,47 @@ static inline void xs_tcp_read_xid(struct sock_xprt *transport, struct xdr_skb_r
 	if (used != len)
 		return;
 	transport->tcp_flags &= ~TCP_RCV_COPY_XID;
-	transport->tcp_flags |= TCP_RCV_COPY_DATA;
+	transport->tcp_flags |= TCP_RCV_COPY_CALLDIR;
 	transport->tcp_copied = 4;
-	dprintk("RPC:       reading reply for XID %08x\n",
+	dprintk("RPC:       reading %s XID %08x\n",
+			(transport->tcp_calldir == RPC_REPLY) ? "reply for"
+							      : "request with",
 			ntohl(transport->tcp_xid));
 	xs_tcp_check_fraghdr(transport);
 }
 
+static inline void xs_tcp_read_calldir(struct sock_xprt *transport,
+				       struct xdr_skb_reader *desc)
+{
+	size_t len, used;
+	u32 offset;
+	char *p;
+
+	/*
+	 * We want transport->tcp_offset to be 8 at the end of this routine
+	 * (4 bytes for the xid and 4 bytes for the call/reply flag).
+	 * When this function is called for the first time,
+	 * transport->tcp_offset is 4 (after having already read the xid).
+	 */
+	offset = transport->tcp_offset - sizeof(transport->tcp_xid);
+	len = sizeof(transport->tcp_calldir) - offset;
+	dprintk("RPC:       reading CALL/REPLY flag (%Zu bytes)\n", len);
+	p = ((char *) &transport->tcp_calldir) + offset;
+	used = xdr_skb_read_bits(desc, p, len);
+	transport->tcp_offset += used;
+	if (used != len)
+		return;
+	transport->tcp_flags &= ~TCP_RCV_COPY_CALLDIR;
+	transport->tcp_flags |= TCP_RCV_COPY_DATA;
+	transport->tcp_copied += 4;
+	transport->tcp_calldir = ntohl(transport->tcp_calldir);
+	dprintk("RPC:       reading %s CALL/REPLY flag %08x\n",
+			(transport->tcp_calldir == RPC_REPLY) ? "reply for"
+							      : "request with",
+							transport->tcp_calldir);
+	xs_tcp_check_fraghdr(transport);
+}
+
 static inline void xs_tcp_read_request(struct rpc_xprt *xprt, struct xdr_skb_reader *desc)
 {
 	struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
@@ -1077,6 +1113,11 @@ static int xs_tcp_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, uns
 			xs_tcp_read_xid(transport, &desc);
 			continue;
 		}
+		/* Read in the call/reply flag */
+		if (transport->tcp_flags & TCP_RCV_COPY_CALLDIR) {
+			xs_tcp_read_calldir(transport, &desc);
+			continue;
+		}
 		/* Read in the request data */
 		if (transport->tcp_flags & TCP_RCV_COPY_DATA) {
 			xs_tcp_read_request(xprt, &desc);
Signature · 1 line
1.6.0.2
#77 lore raw

Re: [pnfs] [RFC 53/85] nfs41: Add ability to read RPC call direction on TCP stream.

Quoted 74 lines · Benny Halevy

NFSv4.1 callbacks can arrive over an existing connection. This patch adds the logic to read the RPC call direction (call or reply). It does this by updating the state machine to look for the call direction invoking xs_tcp_read_calldir(...) after reading the XID.

To avoid confusion, keep tcp_calldir in code in host order.

---
 net/sunrpc/xprtsock.c |   49 +++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 45 insertions(+), 4 deletions(-)
net/sunrpc/xprtsock.c +23 -4
index 0a50361..1ce05d7 100644
@@ -222,7 +222,8 @@ struct sock_xprt {
 	 * State of TCP reply receive
 	 */
 	__be32			tcp_fraghdr,
-				tcp_xid;
+				tcp_xid,
+				tcp_calldir;	/* Is this a call or a reply? */
 
 	u32			tcp_offset,
 				tcp_reclen;
@@ -259,6 +260,7 @@ struct sock_xprt {
 #define TCP_RCV_COPY_FRAGHDR	(1UL << 1)
 #define TCP_RCV_COPY_XID	(1UL << 2)
 #define TCP_RCV_COPY_DATA	(1UL << 3)
+#define TCP_RCV_COPY_CALLDIR	(1UL << 4)
 
 static inline struct sockaddr *xs_addr(struct rpc_xprt *xprt)
 {
@@ -919,7 +921,7 @@ static inline void xs_tcp_read_fraghdr(struct rpc_xprt *xprt, struct xdr_skb_rea
 	transport->tcp_offset = 0;
 
 	/* Sanity check of the record length */
-	if (unlikely(transport->tcp_reclen < 4)) {
+	if (unlikely(transport->tcp_reclen < 8)) {
 		dprintk("RPC:       invalid TCP record fragment length\n");
 		xprt_force_disconnect(xprt);
 		return;
@@ -954,13 +956,47 @@ static inline void xs_tcp_read_xid(struct sock_xprt *transport, struct xdr_skb_r
 	if (used != len)
 		return;
 	transport->tcp_flags &= ~TCP_RCV_COPY_XID;
-	transport->tcp_flags |= TCP_RCV_COPY_DATA;
+	transport->tcp_flags |= TCP_RCV_COPY_CALLDIR;
 	transport->tcp_copied = 4;
-	dprintk("RPC:       reading reply for XID %08x\n",
+	dprintk("RPC:       reading %s XID %08x\n",
+			(transport->tcp_calldir == RPC_REPLY) ? "reply for"
+							      : "request with",
 			ntohl(transport->tcp_xid));
 	xs_tcp_check_fraghdr(transport);
 }
 
+static inline void xs_tcp_read_calldir(struct sock_xprt *transport,
+				       struct xdr_skb_reader *desc)
+{
+	size_t len, used;
+	u32 offset;
+	char *p;
+
+	/*
+	 * We want transport->tcp_offset to be 8 at the end of this routine
+	 * (4 bytes for the xid and 4 bytes for the call/reply flag).
+	 * When this function is called for the first time,
+	 * transport->tcp_offset is 4 (after having already read the xid).
+	 */
+	offset = transport->tcp_offset - sizeof(transport->tcp_xid);
+	len = sizeof(transport->tcp_calldir) - offset;

net/sunrpc/xprtsock.c static inline void xs_tcp_read_xid(struct sock_xprt *transport, struct xdr_skb_r · line 992

Quoted 10 lines
net/sunrpc/xprtsock.c +10 -0
+	dprintk("RPC:       reading CALL/REPLY flag (%Zu bytes)\n", len);
+	p = ((char *) &transport->tcp_calldir) + offset;
+	used = xdr_skb_read_bits(desc, p, len);
+	transport->tcp_offset += used;
+	if (used != len)
+		return;
+	transport->tcp_flags &= ~TCP_RCV_COPY_CALLDIR;
+	transport->tcp_flags |= TCP_RCV_COPY_DATA;
+	transport->tcp_copied += 4;
+	transport->tcp_calldir = ntohl(transport->tcp_calldir);

review 11-14: introduce direction flag, no need to save the actual value of calldir.

Quoted 22 lines
net/sunrpc/xprtsock.c +12 -0
+	dprintk("RPC:       reading %s CALL/REPLY flag %08x\n",
+			(transport->tcp_calldir == RPC_REPLY) ? "reply for"
+							      : "request with",
+							transport->tcp_calldir);
+	xs_tcp_check_fraghdr(transport);
+}
+
 static inline void xs_tcp_read_request(struct rpc_xprt *xprt, struct xdr_skb_reader *desc)
 {
 	struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
@@ -1077,6 +1113,11 @@ static int xs_tcp_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, uns
 			xs_tcp_read_xid(transport, &desc);
 			continue;
 		}
+		/* Read in the call/reply flag */
+		if (transport->tcp_flags & TCP_RCV_COPY_CALLDIR) {
+			xs_tcp_read_calldir(transport, &desc);
+			continue;
+		}
 		/* Read in the request data */
 		if (transport->tcp_flags & TCP_RCV_COPY_DATA) {
 			xs_tcp_read_request(xprt, &desc);
#78 lore raw

RFC 54/85 nfs41: Skip past the RPC call direction

In reply to Benny Halevy

From: Ricardo Labiaga <[email protected]>

xs_tcp_read_calldir() is now responsible for reading the RPC call direction and determining whether it is a reply or a callback request. The call to xdr_skb_read_bits() inside xs_tcp_read_calldir() moves the xdr_skb_reader offset past the RPC call direction (offset should be equal to 8). Therefore xs_tcp_read_common() called from xs_tcp_ready_reply() should be copying the TCP buffer starting past the RPC call direction. It is now necessary to read the RPC call direction earlier to determine whether to call the reply handler or the callback handler.

call_verify() should therefore skip past the XID and call/reply flag.

---
 net/sunrpc/clnt.c |   13 +++++++------
 1 files changed, 7 insertions(+), 6 deletions(-)
net/sunrpc/clnt.c +7 -6
index c31ded3..dd0ff2d 100644
@@ -1371,13 +1371,14 @@ rpc_verify_header(struct rpc_task *task)
 	}
 	if ((len -= 3) < 0)
 		goto out_overflow;
-	p += 1;	/* skip XID */
 
-	if ((n = ntohl(*p++)) != RPC_REPLY) {
-		dprintk("RPC: %5u %s: not an RPC reply: %x\n",
-				task->tk_pid, __func__, n);
-		goto out_garbage;
-	}
+	/*
+	 * Skip the XID and call direction.
+	 * The underlying transport has read the XID and RPC call direction
+	 * to determine this is an RPC reply.
+	 */
+	p += 2;
+
 	if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) {
 		if (--len < 0)
 			goto out_overflow;
Signature · 1 line
1.6.0.2
#79 lore raw

RFC 55/85 nfs41: Refactor NFSv4 callback service

In reply to Benny Halevy

Prepare the NFSv4 callback service mechanism to understand v4.0 and v4.1 callbacks. Create a new function nfs4_callback_up() that contains the minor version 0 specific logic. It is invoked by nfs_callback_up().

Augment the list of arguments passed to nfs_callback_up() since it will need to deal with minor version 1 as well. Move the creation of the backchannel after the rpc_client has been initialized since the rpc_xprt structure will be needed to crated the NFSv4.1 callback.

Keep a pointer to the callback_svc that will be set as per the minor version used.

---
 fs/nfs/callback.c |   62 +++++++++++++++++++++++++++++++++++++---------------
 fs/nfs/callback.h |    2 +-
 fs/nfs/client.c   |   23 ++++++++++---------
 3 files changed, 57 insertions(+), 30 deletions(-)
fs/nfs/callback.c +44 -18
index c2e9cfd..e792706 100644
@@ -67,7 +67,7 @@ module_param_call(callback_tcpport, param_set_port, param_get_int,
  * This is the callback kernel thread.
  */
 static int
-nfs_callback_svc(void *vrqstp)
+nfs4_callback_svc(void *vrqstp)
 {
 	int err, preverr = 0;
 	struct svc_rqst *rqstp = vrqstp;
@@ -104,12 +104,37 @@ nfs_callback_svc(void *vrqstp)
 	return 0;
 }
 
+
+/*
+ * Prepare to bring up the NFSv4 callback service
+ */
+struct svc_rqst *
+nfs4_callback_up(struct svc_serv *serv)
+{
+	int ret;
+
+	ret = svc_create_xprt(serv, "tcp", nfs_callback_set_tcpport,
+			      SVC_SOCK_ANONYMOUS);
+	if (unlikely(ret <= 0)) {
+		if (ret == 0)
+			ret = -EIO;
+		return ERR_PTR(ret);
+	}
+	nfs_callback_tcpport = ret;
+	dprintk("NFS: Callback listener port = %u (af %u)\n",
+			nfs_callback_tcpport, nfs_callback_family);
+	return svc_prepare_thread(serv, &serv->sv_pools[0]);
+}
+
 /*
  * Bring up the callback thread if it is not already up.
  */
-int nfs_callback_up(void)
+int nfs_callback_up(u32 minorversion, void *args)
 {
 	struct svc_serv *serv = NULL;
+	struct svc_rqst *rqstp;
+	int (* callback_svc)(void *vrqstp);
+	char svc_name[12];
 	int ret = 0;
 
 	mutex_lock(&nfs_callback_mutex);
@@ -117,30 +142,31 @@ int nfs_callback_up(void)
 		goto out;
 	serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE,
 				nfs_callback_family, NULL);
-	ret = -ENOMEM;
-	if (!serv)
-		goto out_err;
-
-	ret = svc_create_xprt(serv, "tcp", nfs_callback_set_tcpport,
-			      SVC_SOCK_ANONYMOUS);
-	if (ret <= 0)
+	if (!serv) {
+		ret = -ENOMEM;
 		goto out_err;
-	nfs_callback_tcpport = ret;
-	dprintk("NFS: Callback listener port = %u (af %u)\n",
-			nfs_callback_tcpport, nfs_callback_family);
+	}
 
-	nfs_callback_info.rqst = svc_prepare_thread(serv, &serv->sv_pools[0]);
-	if (IS_ERR(nfs_callback_info.rqst)) {
-		ret = PTR_ERR(nfs_callback_info.rqst);
-		nfs_callback_info.rqst = NULL;
+	/* FIXME: either 4.0 or 4.1 callback service can be up at a time
+	 * need to monitor and control them both */
+	if (!minorversion) {
+		rqstp = nfs4_callback_up(serv);
+		callback_svc = nfs4_callback_svc;
+	} else {
+		BUG();	/* for now */
+	}
+	if (IS_ERR(rqstp)) {
+		ret = PTR_ERR(rqstp);
 		goto out_err;
 	}
 
 	svc_sock_update_bufs(serv);
 
-	nfs_callback_info.task = kthread_run(nfs_callback_svc,
+	sprintf(svc_name, "nfsv4.%u-svc", minorversion);
+	nfs_callback_info.rqst = rqstp;
+	nfs_callback_info.task = kthread_run(callback_svc,
 					     nfs_callback_info.rqst,
-					     "nfsv4-svc");
+					     svc_name);
 	if (IS_ERR(nfs_callback_info.task)) {
 		ret = PTR_ERR(nfs_callback_info.task);
 		svc_exit_thread(nfs_callback_info.rqst);
fs/nfs/callback.h +1 -1
index bb25d21..a2b3ce4 100644
@@ -63,7 +63,7 @@ extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getat
 extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy);
 
 #ifdef CONFIG_NFS_V4
-extern int nfs_callback_up(void);
+extern int nfs_callback_up(u32 minorversion, void *args);
 extern void nfs_callback_down(void);
 #else
 #define nfs_callback_up()	(0)
fs/nfs/client.c +12 -11
index c69424d..1c0cae9 100644
@@ -129,12 +129,6 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
 
 	clp->rpc_ops = cl_init->rpc_ops;
 
-	if (cl_init->rpc_ops->version == 4) {
-		if (nfs_callback_up() < 0)
-			goto error_2;
-		__set_bit(NFS_CS_CALLBACK, &clp->cl_res_state);
-	}
-
 	atomic_set(&clp->cl_count, 1);
 	clp->cl_cons_state = NFS_CS_INITING;
 
@@ -144,7 +138,7 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
 	if (cl_init->hostname) {
 		clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL);
 		if (!clp->cl_hostname)
-			goto error_3;
+			goto error_cleanup;
 	}
 
 	INIT_LIST_HEAD(&clp->cl_superblocks);
@@ -167,10 +161,7 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
 
 	return clp;
 
-error_3:
-	if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
-		nfs_callback_down();
-error_2:
+error_cleanup:
 	kfree(clp);
 error_0:
 	return NULL;
@@ -1008,6 +999,16 @@ static int nfs4_init_client(struct nfs_client *clp,
 		goto error;
 	memcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr));
 
+	/* Start the callback server */
+	error = nfs_callback_up(clp->cl_minorversion,
+		clp->cl_rpcclient->cl_xprt);
+	if (error < 0) {
+		dprintk("%s: failed to start callback. Error = %d\n",
+			__func__, error);
+		goto error;
+	}
+	__set_bit(NFS_CS_CALLBACK, &clp->cl_res_state);
+
 	error = nfs_idmap_new(clp);
 	if (error < 0) {
 		dprintk("%s: failed to create idmapper. Error = %d\n",
Signature · 1 line
1.6.0.2
#80 lore raw

RFC 56/85 nfs41: client callback structures

In reply to Benny Halevy

From: Ricardo Labiaga <[email protected]>

Adds new list of rpc_xprt structures, and a readers/writers lock to protect the list. The list is used to preallocate resources for the backchannel during backchannel requests. Callbacks are not expected to cause significant latency, so only one callback will be allowed at this time.

It also adds a pointer to the NFS callback service so that requests can be directed to it for processing.

nfs41: NFSv4.1 callback support

New callback members added to svc_serv. The NFSv4.1 callback service will sleep on the svc_serv->svc_cb_waitq until new callback requests arrive. The request will be queued in svc_serv->svc_cb_list. This patch adds this list, the sleep queue and spinlock to svc_serv.

---
 include/linux/sunrpc/svc.h  |   10 ++++++++++
 include/linux/sunrpc/xprt.h |   22 ++++++++++++++++++++++
 2 files changed, 32 insertions(+), 0 deletions(-)
include/linux/sunrpc/svc.h +10 -0
index 3afe7fb..1d3b971 100644
@@ -81,6 +81,16 @@ struct svc_serv {
 	struct module *		sv_module;	/* optional module to count when
 						 * adding threads */
 	svc_thread_fn		sv_function;	/* main function for threads */
+#if defined(CONFIG_NFS_V4_1)
+	struct list_head 	sv_cb_list;	/* queue for callback requests
+						 * that arrive over the same
+						 * connection
+						 */
+	spinlock_t		sv_cb_lock;	/* protects the svc_cb_list */
+	wait_queue_head_t	sv_cb_waitq;	/* sleep here if there are no
+						 * entries in the svc_cb_list
+						 */
+#endif /* CONFIG_NFS_V4_1 */
 };
 
 /*
include/linux/sunrpc/xprt.h +22 -0
index 11fc71d..c5d3c7c 100644
@@ -97,6 +97,12 @@ struct rpc_rqst {
 
 	unsigned long		rq_xtime;	/* when transmitted */
 	int			rq_ntrans;
+
+#if defined(CONFIG_NFS_V4_1)
+	struct list_head	rq_bc_list;	/* Callback service list */
+	unsigned long		rq_bc_pa_state;	/* Backchannel prealloc state */
+	struct list_head	rq_bc_pa_list;	/* Backchannel prealloc list */
+#endif /* CONFIG_NFS_V4_1 */
 };
 #define rq_svec			rq_snd_buf.head
 #define rq_slen			rq_snd_buf.len
@@ -174,6 +180,14 @@ struct rpc_xprt {
 	spinlock_t		reserve_lock;	/* lock slot table */
 	u32			xid;		/* Next XID value to use */
 	struct rpc_task *	snd_task;	/* Task blocked in send */
+#if defined(CONFIG_NFS_V4_1)
+	struct svc_serv		*bc_serv;	/* The RPC service which will */
+						/* process the callback */
+	spinlock_t		bc_pa_lock;	/* Protects the preallocated */
+						/* items */
+	struct list_head	bc_pa_list;	/* List of preallocated */
+						/* backchannel rpc_rqst's */
+#endif /* CONFIG_NFS_V4_1 */
 	struct list_head	recv;
 
 	struct {
@@ -192,6 +206,14 @@ struct rpc_xprt {
 	const char		*address_strings[RPC_DISPLAY_MAX];
 };
 
+#if defined(CONFIG_NFS_V4_1)
+/*
+ * Backchannel flags
+ */
+#define	RPC_BC_PA_IN_USE	0x0001		/* Preallocated backchannel */
+						/* buffer in use */
+#endif /* CONFIG_NFS_V4_1 */
+
 struct xprt_create {
 	int			ident;		/* XPRT_TRANSPORT identifier */
 	struct sockaddr *	srcaddr;	/* optional local address */
Signature · 1 line
1.6.0.2
#81 lore raw

RFC 57/85 nfs41: Initialize new rpc_xprt callback related fields

In reply to Benny Halevy

From: Ricardo Labiaga <[email protected]>

---
 net/sunrpc/xprt.c |    5 +++++
 1 files changed, 5 insertions(+), 0 deletions(-)
net/sunrpc/xprt.c +5 -0
index 29e401b..848a7af 100644
@@ -1034,6 +1034,11 @@ found:
 
 	INIT_LIST_HEAD(&xprt->free);
 	INIT_LIST_HEAD(&xprt->recv);
+#if defined(CONFIG_NFS_V4_1)
+	spin_lock_init(&xprt->bc_pa_lock);
+	INIT_LIST_HEAD(&xprt->bc_pa_list);
+#endif /* CONFIG_NFS_V4_1 */
+
 	INIT_WORK(&xprt->task_cleanup, xprt_autoclose);
 	setup_timer(&xprt->timer, xprt_init_autodisconnect,
 			(unsigned long)xprt);
Signature · 1 line
1.6.0.2
#82 lore raw

RFC 58/85 nfs41: New backchannel helper routines

In reply to Benny Halevy

From: Ricardo Labiaga <[email protected]>

This patch introduces support to setup the callback xprt on the client side. It allocates/ destroys the preallocated memory structures used to process backchannel requests.

At setup time, xprt_setup_backchannel() is invoked to allocate one or more rpc_rqst structures and substructures. This ensures that they are available when an RPC callback arrives. The rpc_rqst structures are maintained in a linked list attached to the rpc_xprt structure. We keep track of the number of allocations so that they can be correctly removed when the channel is destroyed.

When an RPC callback arrives, xprt_alloc_bc_request() is invoked to obtain a preallocated rpc_rqst structure. An rpc_xprt structure is returned, and its RPC_BC_PREALLOC_IN_USE bit is set in rpc_xprt->bc_flags. The structure is removed from the the list since it is now in use, and it will be later added back when its user is done with it.

After the RPC callback replies, the rpc_rqst structure is returned by invoking xprt_free_bc_request(). This clears the RPC_BC_PREALLOC_IN_USE bit and adds it back to the list, allowing it to be reused by a subsequent RPC callback request.

---
 include/linux/sunrpc/xprt.h   |    1 +
 net/sunrpc/Makefile           |    1 +
 net/sunrpc/backchannel_rqst.c |  268 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 270 insertions(+), 0 deletions(-)
 create mode 100644 net/sunrpc/backchannel_rqst.c
include/linux/sunrpc/xprt.h +1 -0
index c5d3c7c..035431b 100644
@@ -183,6 +183,7 @@ struct rpc_xprt {
 #if defined(CONFIG_NFS_V4_1)
 	struct svc_serv		*bc_serv;	/* The RPC service which will */
 						/* process the callback */
+	unsigned int		bc_alloc_count;	/* Total number of preallocs */
 	spinlock_t		bc_pa_lock;	/* Protects the preallocated */
 						/* items */
 	struct list_head	bc_pa_list;	/* List of preallocated */
net/sunrpc/Makefile +1 -0
index 5369aa3..4a01f96 100644
@@ -13,5 +13,6 @@ sunrpc-y := clnt.o xprt.o socklib.o xprtsock.o sched.o \
 	    rpcb_clnt.o timer.o xdr.o \
 	    sunrpc_syms.o cache.o rpc_pipe.o \
 	    svc_xprt.o
+sunrpc-$(CONFIG_NFS_V4_1) += backchannel_rqst.o
 sunrpc-$(CONFIG_PROC_FS) += stats.o
 sunrpc-$(CONFIG_SYSCTL) += sysctl.o
net/sunrpc/backchannel_rqst.c new file +268 -0

268 lines not on this page · raw message

Signature · 1 line
1.6.0.2
#83 lore raw

RFC 59/85 nfs41: New include/linux/sunrpc/bc_xprt.h

In reply to Benny Halevy

From: Ricardo Labiaga <[email protected]>

Contains prototype for backchannel helper routines.

---
 include/linux/sunrpc/bc_xprt.h |   40 ++++++++++++++++++++++++++++++++++++++++
 1 files changed, 40 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/sunrpc/bc_xprt.h
include/linux/sunrpc/bc_xprt.h new file +40 -0
new file mode 100644
index 0000000..8ad9337
@@ -0,0 +1,40 @@
+/******************************************************************************
+
+(c) 2008 Network Appliance, Inc.  All Rights Reserved.
+
+Network Appliance provides this source code under the GPL v2 License.
+The GPL v2 license is available at
+http://opensource.org/licenses/gpl-license.php.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+/*
+ * Functions to create and manage the backchannel
+ */
+
+#ifndef _LINUX_SUNRPC_BC_XPRT_H
+#define _LINUX_SUNRPC_BC_XPRT_H
+
+#include <linux/sunrpc/svcsock.h>
+#include <linux/sunrpc/xprt.h>
+
+#ifdef CONFIG_NFS_V4_1
+struct rpc_rqst *xprt_alloc_bc_request(struct rpc_xprt *xprt);
+void xprt_free_bc_request(struct rpc_rqst *req);
+int xprt_setup_backchannel(struct rpc_xprt *, unsigned int min_reqs);
+void xprt_destroy_backchannel(struct rpc_xprt *, int max_reqs);
+#endif /* CONFIG_NFS_V4_1 */
+#endif /* _LINUX_SUNRPC_BC_XPRT_H */
+
Signature · 1 line
1.6.0.2
#84 lore raw

RFC 60/85 nfs41: New xs_tcp_read_data()

In reply to Benny Halevy

Handles RPC replies and backchannel callbacks. Traditionally the NFS client has expected only RPC replies on its open connections. With NFSv4.1, callbacks can arrive over an existing open connection.

This patch refactors the old xs_tcp_read_request() into an RPC reply handler:
xs_tcp_read_reply(), a new backchannel callback handler: xs_tcp_read_callback(), and a common routine to read the data off the transport: xs_tcp_read_common(). The new xs_tcp_read_callback() queues callback requests onto a queue where the callback service (a separate thread) is listening for the processing.

This patch incorporates work and suggestions from Rahul Iyer ([email protected]) and Benny Halevy ([email protected]).

Note that we need to update the printk warning message to throttle the number of warnings printed to avoid overflowing syslog with it.

---
 net/sunrpc/xprtsock.c |  139 ++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 121 insertions(+), 18 deletions(-)
net/sunrpc/xprtsock.c +121 -18
index 1ce05d7..6ccdec8 100644
@@ -34,6 +34,9 @@
 #include <linux/sunrpc/sched.h>
 #include <linux/sunrpc/xprtsock.h>
 #include <linux/file.h>
+#ifdef CONFIG_NFS_V4_1
+#include <linux/sunrpc/bc_xprt.h>
+#endif
 
 #include <net/sock.h>
 #include <net/checksum.h>
@@ -997,25 +1000,16 @@ static inline void xs_tcp_read_calldir(struct sock_xprt *transport,
 	xs_tcp_check_fraghdr(transport);
 }
 
-static inline void xs_tcp_read_request(struct rpc_xprt *xprt, struct xdr_skb_reader *desc)
+static inline void xs_tcp_read_common(struct rpc_xprt *xprt,
+				     struct xdr_skb_reader *desc,
+				     struct rpc_rqst *req)
 {
-	struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
-	struct rpc_rqst *req;
+	struct sock_xprt *transport =
+				container_of(xprt, struct sock_xprt, xprt);
 	struct xdr_buf *rcvbuf;
 	size_t len;
 	ssize_t r;
 
-	/* Find and lock the request corresponding to this xid */
-	spin_lock(&xprt->transport_lock);
-	req = xprt_lookup_rqst(xprt, transport->tcp_xid);
-	if (!req) {
-		transport->tcp_flags &= ~TCP_RCV_COPY_DATA;
-		dprintk("RPC:       XID %08x request not found!\n",
-				ntohl(transport->tcp_xid));
-		spin_unlock(&xprt->transport_lock);
-		return;
-	}
-
 	rcvbuf = &req->rq_private_buf;
 	len = desc->count;
 	if (len > transport->tcp_reclen - transport->tcp_offset) {
@@ -1053,7 +1047,7 @@ static inline void xs_tcp_read_request(struct rpc_xprt *xprt, struct xdr_skb_rea
 				"tcp_offset = %u, tcp_reclen = %u\n",
 				xprt, transport->tcp_copied,
 				transport->tcp_offset, transport->tcp_reclen);
-		goto out;
+		return;
 	}
 
 	dprintk("RPC:       XID %08x read %Zd bytes\n",
@@ -1069,11 +1063,120 @@ static inline void xs_tcp_read_request(struct rpc_xprt *xprt, struct xdr_skb_rea
 			transport->tcp_flags &= ~TCP_RCV_COPY_DATA;
 	}
 
-out:
+	return;
+}
+
+/*
+ * Finds the request corresponding to the RPC xid and invokes the common
+ * tcp read code to read the data.
+ */
+static inline int xs_tcp_read_reply(struct rpc_xprt *xprt,
+				    struct xdr_skb_reader *desc)
+{
+	struct sock_xprt *transport =
+				container_of(xprt, struct sock_xprt, xprt);
+	struct rpc_rqst *req;
+
+	dprintk("RPC:       read reply XID %08x\n", ntohl(transport->tcp_xid));
+
+	/* Find and lock the request corresponding to this xid */
+	spin_lock(&xprt->transport_lock);
+	req = xprt_lookup_rqst(xprt, transport->tcp_xid);
+	if (!req) {
+		dprintk("RPC:       XID %08x request not found!\n",
+				ntohl(transport->tcp_xid));
+		spin_unlock(&xprt->transport_lock);
+		return -1;
+	}
+
+	xs_tcp_read_common(xprt, desc, req);
+
 	if (!(transport->tcp_flags & TCP_RCV_COPY_DATA))
 		xprt_complete_rqst(req->rq_task, transport->tcp_copied);
+
 	spin_unlock(&xprt->transport_lock);
-	xs_tcp_check_fraghdr(transport);
+	return 0;
+}
+
+#if defined(CONFIG_NFS_V4_1)
+/*
+ * Obtains an rpc_rqst previously allocated and invokes the common
+ * tcp read code to read the data.  The result is placed in the callback
+ * queue.
+ * If we're unable to obtain the rpc_rqst we schedule the closing of the
+ * connection and return -1.
+ */
+static inline int xs_tcp_read_callback(struct rpc_xprt *xprt,
+				       struct xdr_skb_reader *desc)
+{
+	struct sock_xprt *transport =
+				container_of(xprt, struct sock_xprt, xprt);
+	struct rpc_rqst *req;
+
+	req = xprt_alloc_bc_request(xprt);
+	if (req == NULL) {
+		/*
+		 * Schedule an autoclose RPC call
+		 */
+		printk(KERN_WARNING "Callback slot table overflowed\n");
+		set_bit(XPRT_CLOSE_WAIT, &xprt->state);
+		if (test_and_set_bit(XPRT_LOCKED, &xprt->state) == 0)
+			queue_work(rpciod_workqueue, &xprt->task_cleanup);
+		return -1;
+	}
+
+	req->rq_xid = transport->tcp_xid;
+	dprintk("RPC:       read callback  XID %08x\n", ntohl(req->rq_xid));
+	xs_tcp_read_common(xprt, desc, req);
+
+	if (!(transport->tcp_flags & TCP_RCV_COPY_DATA)) {
+		struct svc_serv *bc_serv = xprt->bc_serv;
+
+		/*
+		 * Add callback request to callback list.  The callback
+		 * service sleeps on the sv_cb_waitq waiting for new
+		 * requests.  Wake it up after adding enqueing the
+		 * request.
+		 */
+		dprintk("RPC:       add callback request to list\n");
+		spin_lock(&bc_serv->sv_cb_lock);
+		list_add(&req->rq_bc_list, &bc_serv->sv_cb_list);
+		spin_unlock(&bc_serv->sv_cb_lock);
+		wake_up(&bc_serv->sv_cb_waitq);
+	}
+
+	return 0;
+}
+#endif /* CONFIG_NFS_V4_1 */
+
+/*
+ * Read data off the transport.  This can be either an RPC_CALL or an
+ * RPC_REPLY.  Relay the processing to helper functions.
+ */
+static void xs_tcp_read_data(struct rpc_xprt *xprt,
+				    struct xdr_skb_reader *desc)
+{
+	struct sock_xprt *transport =
+				container_of(xprt, struct sock_xprt, xprt);
+	int status;
+
+#if defined(CONFIG_NFS_V4_1)
+	status = (transport->tcp_calldir == RPC_REPLY) ?
+		xs_tcp_read_reply(xprt, desc) :
+		xs_tcp_read_callback(xprt, desc);
+#else
+	status = xs_tcp_read_reply(xprt, desc);
+#endif /* CONFIG_NFS_V4_1 */
+
+	if (status == 0)
+		xs_tcp_check_fraghdr(transport);
+	else {
+		/*
+		 * The transport_lock protects the request handling.
+		 * There's no need to hold it to update the tcp_flags.
+		 */
+		transport->tcp_flags &= ~TCP_RCV_COPY_DATA;
+	}
 }
 
 static inline void xs_tcp_read_discard(struct sock_xprt *transport, struct xdr_skb_reader *desc)
@@ -1120,7 +1223,7 @@ static int xs_tcp_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, uns
 		}
 		/* Read in the request data */
 		if (transport->tcp_flags & TCP_RCV_COPY_DATA) {
-			xs_tcp_read_request(xprt, &desc);
+			xs_tcp_read_data(xprt, &desc);
 			continue;
 		}
 		/* Skip over any trailing bytes on short reads */
Signature · 1 line
1.6.0.2
#85 lore raw

RFC 61/85 nfs41: Add backchannel processing support to RPC state machine

In reply to Benny Halevy

Adds rpc_run_bc_task() which is called by the NFS callback service to process backchannel requests. It performs similar work to rpc_run_task() though "schedules" the backchannel task to be executed starting at the call_trasmit state in the RPC state machine.

It also introduces some miscellaneous updates to the argument validation, call_transmit, and transport cleanup functions to take into account that there are now forechannel and backchannel tasks.

Backchannel requests do not carry an RPC message structure, since the payload has already been XDR encoded using the existing NFSv4 callback mechanism.

---
 include/linux/sunrpc/sched.h |    2 +
 include/linux/sunrpc/xprt.h  |   12 +++++++++
 net/sunrpc/clnt.c            |   52 ++++++++++++++++++++++++++++++++++++++++-
 net/sunrpc/stats.c           |    6 +++-
 net/sunrpc/sunrpc.h          |   35 ++++++++++++++++++++++++++++
 net/sunrpc/xprt.c            |   36 ++++++++++++++++++++++++-----
 6 files changed, 133 insertions(+), 10 deletions(-)
 create mode 100644 net/sunrpc/sunrpc.h
include/linux/sunrpc/sched.h +2 -0
index 1773768..4010977 100644
@@ -210,6 +210,8 @@ struct rpc_wait_queue {
  */
 struct rpc_task *rpc_new_task(const struct rpc_task_setup *);
 struct rpc_task *rpc_run_task(const struct rpc_task_setup *);
+struct rpc_task *rpc_run_bc_task(struct rpc_rqst *req,
+				const struct rpc_call_ops *ops);
 void		rpc_put_task(struct rpc_task *);
 void		rpc_exit_task(struct rpc_task *);
 void		rpc_release_calldata(const struct rpc_call_ops *, void *);
include/linux/sunrpc/xprt.h +12 -0
index 035431b..45a92e2 100644
@@ -215,6 +215,18 @@ struct rpc_xprt {
 						/* buffer in use */
 #endif /* CONFIG_NFS_V4_1 */
 
+#if defined(CONFIG_NFS_V4_1)
+static inline int bc_prealloc(struct rpc_rqst *req)
+{
+	return test_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state);
+}
+#else
+static inline int bc_prealloc(struct rpc_rqst *req)
+{
+	return 0;
+}
+#endif /* CONFIG_NFS_V4_1 */
+
 struct xprt_create {
 	int			ident;		/* XPRT_TRANSPORT identifier */
 	struct sockaddr *	srcaddr;	/* optional local address */
net/sunrpc/clnt.c +50 -2

73 lines not on this page · raw message

net/sunrpc/stats.c +4 -2
index 50b049c..89dc28e 100644
@@ -141,12 +141,14 @@ EXPORT_SYMBOL_GPL(rpc_free_iostats);
 void rpc_count_iostats(struct rpc_task *task)
 {
 	struct rpc_rqst *req = task->tk_rqstp;
-	struct rpc_iostats *stats = task->tk_client->cl_metrics;
+	struct rpc_iostats *stats;
 	struct rpc_iostats *op_metrics;
 	long rtt, execute, queue;
 
-	if (!stats || !req)
+	if (!task->tk_client || task->tk_client->cl_metrics || !req)
 		return;
+
+	stats = task->tk_client->cl_metrics;
 	op_metrics = &stats[task->tk_msg.rpc_proc->p_statidx];
 
 	op_metrics->om_ops++;
net/sunrpc/sunrpc.h new file +35 -0
new file mode 100644
index 0000000..b462de4
@@ -0,0 +1,35 @@
+/******************************************************************************
+
+(c) 2008 Network Appliance, Inc.  All Rights Reserved.
+
+Network Appliance provides this source code under the GPL v2 License.
+The GPL v2 license is available at
+http://opensource.org/licenses/gpl-license.php.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+/*
+ * Functions and macros used internally by RPC
+ */
+
+#ifndef _NET_SUNRPC_SUNRPC_H
+#define _NET_SUNRPC_SUNRPC_H
+
+#define rpc_reply_expected(task) \
+	(((task)->tk_msg.rpc_proc != NULL) && \
+	((task)->tk_msg.rpc_proc->p_decode != NULL))
+
+#endif /* _NET_SUNRPC_SUNRPC_H */
+
net/sunrpc/xprt.c +30 -6
index 848a7af..cc1e5f2 100644
@@ -12,8 +12,9 @@
  *  -	Next, the caller puts together the RPC message, stuffs it into
  *	the request struct, and calls xprt_transmit().
  *  -	xprt_transmit sends the message and installs the caller on the
- *	transport's wait list. At the same time, it installs a timer that
- *	is run after the packet's timeout has expired.
+ *	transport's wait list. At the same time, if a reply is expected,
+ *	it installs a timer that is run after the packet's timeout has
+ *	expired.
  *  -	When a packet arrives, the data_ready handler walks the list of
  *	pending requests for that transport. If a matching XID is found, the
  *	caller is woken up, and the timer removed.
@@ -46,6 +47,8 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/metrics.h>
 
+#include "sunrpc.h"
+
 /*
  * Local variables
  */
@@ -852,7 +855,10 @@ void xprt_transmit(struct rpc_task *task)
 	dprintk("RPC: %5u xprt_transmit(%u)\n", task->tk_pid, req->rq_slen);
 
 	if (!req->rq_received) {
-		if (list_empty(&req->rq_list)) {
+		if (list_empty(&req->rq_list) && rpc_reply_expected(task)) {
+			/*
+			 * Add to the list only if we're expecting a reply
+			 */
 			spin_lock_bh(&xprt->transport_lock);
 			/* Update the softirq receive buffer */
 			memcpy(&req->rq_private_buf, &req->rq_rcv_buf,
@@ -883,8 +889,13 @@ void xprt_transmit(struct rpc_task *task)
 		/* Don't race with disconnect */
 		if (!xprt_connected(xprt))
 			task->tk_status = -ENOTCONN;
-		else if (!req->rq_received)
+		else if (!req->rq_received && rpc_reply_expected(task)) {
+			/*
+			 * Sleep on the pending queue since
+			 * we're expecting a reply.
+			 */
 			rpc_sleep_on(&xprt->pending, task, xprt_timer);
+		}
 		spin_unlock_bh(&xprt->transport_lock);
 		return;
 	}
@@ -967,11 +978,15 @@ static void xprt_request_init(struct rpc_task *task, struct rpc_xprt *xprt)
  */
 void xprt_release(struct rpc_task *task)
 {
-	struct rpc_xprt	*xprt = task->tk_xprt;
+	struct rpc_xprt	*xprt;
 	struct rpc_rqst	*req;
+	int prealloc;
 
+	BUG_ON(atomic_read(&task->tk_count) < 0);
 	if (!(req = task->tk_rqstp))
 		return;
+	prealloc = bc_prealloc(req);	/* Preallocated backchannel request? */
+	xprt = req->rq_xprt;
 	rpc_count_iostats(task);
 	spin_lock_bh(&xprt->transport_lock);
 	xprt->ops->release_xprt(xprt, task);
@@ -984,10 +999,19 @@ void xprt_release(struct rpc_task *task)
 		mod_timer(&xprt->timer,
 				xprt->last_used + xprt->idle_timeout);
 	spin_unlock_bh(&xprt->transport_lock);
-	xprt->ops->buf_free(req->rq_buffer);
+	if (!bc_prealloc(req))
+		xprt->ops->buf_free(req->rq_buffer);
 	task->tk_rqstp = NULL;
 	if (req->rq_release_snd_buf)
 		req->rq_release_snd_buf(req);
+
+	/*
+	 * Early exit if this is a backchannel preallocated request.
+	 * There is no need to have it added to the RPC slot list.
+	 */
+	if (prealloc)
+		return;
+
 	memset(req, 0, sizeof(*req));	/* mark unused */
 
 	dprintk("RPC: %5u release request %p\n", task->tk_pid, req);
Signature · 1 line
1.6.0.2
#86 lore raw

Re: [pnfs] [RFC 61/85] nfs41: Add backchannel processing support to RPC state machine

Quoted 89 lines · Benny Halevy

Adds rpc_run_bc_task() which is called by the NFS callback service to process backchannel requests. It performs similar work to rpc_run_task() though "schedules" the backchannel task to be executed starting at the call_trasmit state in the RPC state machine.

It also introduces some miscellaneous updates to the argument validation, call_transmit, and transport cleanup functions to take into account that there are now forechannel and backchannel tasks.

Backchannel requests do not carry an RPC message structure, since the payload has already been XDR encoded using the existing NFSv4 callback mechanism.

---
 include/linux/sunrpc/sched.h |    2 +
 include/linux/sunrpc/xprt.h  |   12 +++++++++
 net/sunrpc/clnt.c            |   52 ++++++++++++++++++++++++++++++++++++++++-
 net/sunrpc/stats.c           |    6 +++-
 net/sunrpc/sunrpc.h          |   35 ++++++++++++++++++++++++++++
 net/sunrpc/xprt.c            |   36 ++++++++++++++++++++++++-----
 6 files changed, 133 insertions(+), 10 deletions(-)
 create mode 100644 net/sunrpc/sunrpc.h
include/linux/sunrpc/sched.h +2 -0
index 1773768..4010977 100644
@@ -210,6 +210,8 @@ struct rpc_wait_queue {
  */
 struct rpc_task *rpc_new_task(const struct rpc_task_setup *);
 struct rpc_task *rpc_run_task(const struct rpc_task_setup *);
+struct rpc_task *rpc_run_bc_task(struct rpc_rqst *req,
+				const struct rpc_call_ops *ops);
 void		rpc_put_task(struct rpc_task *);
 void		rpc_exit_task(struct rpc_task *);
 void		rpc_release_calldata(const struct rpc_call_ops *, void *);
include/linux/sunrpc/xprt.h +12 -0
index 035431b..45a92e2 100644
@@ -215,6 +215,18 @@ struct rpc_xprt {
 						/* buffer in use */
 #endif /* CONFIG_NFS_V4_1 */
 
+#if defined(CONFIG_NFS_V4_1)
+static inline int bc_prealloc(struct rpc_rqst *req)
+{
+	return test_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state);
+}
+#else
+static inline int bc_prealloc(struct rpc_rqst *req)
+{
+	return 0;
+}
+#endif /* CONFIG_NFS_V4_1 */
+
 struct xprt_create {
 	int			ident;		/* XPRT_TRANSPORT identifier */
 	struct sockaddr *	srcaddr;	/* optional local address */
net/sunrpc/clnt.c +12 -0
index dd0ff2d..f5418ac 100644
@@ -36,7 +36,9 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/rpc_pipe_fs.h>
 #include <linux/sunrpc/metrics.h>
+#include <linux/sunrpc/bc_xprt.h>
 
+#include "sunrpc.h"
 
 #ifdef RPC_DEBUG
 # define RPCDBG_FACILITY	RPCDBG_CALL
@@ -602,6 +604,51 @@ rpc_call_async(struct rpc_clnt *clnt, const struct rpc_message *msg, int flags,
 }
 EXPORT_SYMBOL_GPL(rpc_call_async);
 
+#if defined(CONFIG_NFS_V4_1)
+/**
+ * rpc_run_bc_task - Allocate a new RPC task for backchannel use, then run
+ * rpc_execute against it
+ * @ops: RPC call ops
+ */
+struct rpc_task *rpc_run_bc_task(struct rpc_rqst *req,
+					const struct rpc_call_ops *tk_ops)
+{
+	struct rpc_task *task;

net/sunrpc/clnt.c rpc_call_async(struct rpc_clnt *clnt, const struct rpc_message *msg, int flags, · line 626

Quoted 10 lines
net/sunrpc/clnt.c +10 -0
+	struct xdr_buf *xbufp = &req->rq_snd_buf;
+	struct rpc_task_setup task_setup_data = {
+		.callback_ops = tk_ops,
+	};
+
+	dprintk("RPC: rpc_run_bc_task req= %p\n", req);
+	/*
+	 * Create an rpc_task to send the data
+	 */
+	task = rpc_new_task(&task_setup_data);

review 11-14: we can't do that.
Trond suggests to factor out what we need in call_transmit i.e. never call call_status. (only error we need to handle is -EAGAIN

setting up a task for that is the easiest way to go.
we should be able to reuse the existing marshalled stuff that the svc prepared rather than copying the data.

Quoted 194 lines
net/sunrpc/clnt.c +28 -2
+	if (!task) {
+		xprt_free_bc_request(req);
+		goto out;
+	}
+	task->tk_rqstp = req;
+
+	/*
+	 * Set up the xdr_buf length.
+	 * This also indicates that the buffer is XDR encoded already.
+	 */
+	xbufp->len = xbufp->head[0].iov_len + xbufp->page_len +
+			xbufp->tail[0].iov_len;
+
+	task->tk_action = call_transmit;
+	atomic_inc(&task->tk_count);
+	BUG_ON(atomic_read(&task->tk_count) != 2);
+	rpc_execute(task);
+
+out:
+	dprintk("RPC: rpc_run_bc_task: task= %p\n", task);
+	return task;
+}
+EXPORT_SYMBOL_GPL(rpc_run_bc_task);
+#endif /* CONFIG_NFS_V4_1 */
+
 void
 rpc_call_start(struct rpc_task *task)
 {
@@ -1094,10 +1141,11 @@ call_transmit(struct rpc_task *task)
 	 * in order to allow access to the socket to other RPC requests.
 	 */
 	call_transmit_status(task);
-	if (task->tk_msg.rpc_proc->p_decode != NULL)
+	if (rpc_reply_expected(task))
 		return;
 	task->tk_action = rpc_exit_task;
-	rpc_wake_up_queued_task(&task->tk_xprt->pending, task);
+	if (task->tk_client != NULL)
+		rpc_wake_up_queued_task(&task->tk_xprt->pending, task);
 }
 
 /*
net/sunrpc/stats.c +4 -2
index 50b049c..89dc28e 100644
@@ -141,12 +141,14 @@ EXPORT_SYMBOL_GPL(rpc_free_iostats);
 void rpc_count_iostats(struct rpc_task *task)
 {
 	struct rpc_rqst *req = task->tk_rqstp;
-	struct rpc_iostats *stats = task->tk_client->cl_metrics;
+	struct rpc_iostats *stats;
 	struct rpc_iostats *op_metrics;
 	long rtt, execute, queue;
 
-	if (!stats || !req)
+	if (!task->tk_client || task->tk_client->cl_metrics || !req)
 		return;
+
+	stats = task->tk_client->cl_metrics;
 	op_metrics = &stats[task->tk_msg.rpc_proc->p_statidx];
 
 	op_metrics->om_ops++;
net/sunrpc/sunrpc.h new file +35 -0
new file mode 100644
index 0000000..b462de4
@@ -0,0 +1,35 @@
+/******************************************************************************
+
+(c) 2008 Network Appliance, Inc.  All Rights Reserved.
+
+Network Appliance provides this source code under the GPL v2 License.
+The GPL v2 license is available at
+http://opensource.org/licenses/gpl-license.php.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+/*
+ * Functions and macros used internally by RPC
+ */
+
+#ifndef _NET_SUNRPC_SUNRPC_H
+#define _NET_SUNRPC_SUNRPC_H
+
+#define rpc_reply_expected(task) \
+	(((task)->tk_msg.rpc_proc != NULL) && \
+	((task)->tk_msg.rpc_proc->p_decode != NULL))
+
+#endif /* _NET_SUNRPC_SUNRPC_H */
+
net/sunrpc/xprt.c +30 -6
index 848a7af..cc1e5f2 100644
@@ -12,8 +12,9 @@
  *  -	Next, the caller puts together the RPC message, stuffs it into
  *	the request struct, and calls xprt_transmit().
  *  -	xprt_transmit sends the message and installs the caller on the
- *	transport's wait list. At the same time, it installs a timer that
- *	is run after the packet's timeout has expired.
+ *	transport's wait list. At the same time, if a reply is expected,
+ *	it installs a timer that is run after the packet's timeout has
+ *	expired.
  *  -	When a packet arrives, the data_ready handler walks the list of
  *	pending requests for that transport. If a matching XID is found, the
  *	caller is woken up, and the timer removed.
@@ -46,6 +47,8 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/metrics.h>
 
+#include "sunrpc.h"
+
 /*
  * Local variables
  */
@@ -852,7 +855,10 @@ void xprt_transmit(struct rpc_task *task)
 	dprintk("RPC: %5u xprt_transmit(%u)\n", task->tk_pid, req->rq_slen);
 
 	if (!req->rq_received) {
-		if (list_empty(&req->rq_list)) {
+		if (list_empty(&req->rq_list) && rpc_reply_expected(task)) {
+			/*
+			 * Add to the list only if we're expecting a reply
+			 */
 			spin_lock_bh(&xprt->transport_lock);
 			/* Update the softirq receive buffer */
 			memcpy(&req->rq_private_buf, &req->rq_rcv_buf,
@@ -883,8 +889,13 @@ void xprt_transmit(struct rpc_task *task)
 		/* Don't race with disconnect */
 		if (!xprt_connected(xprt))
 			task->tk_status = -ENOTCONN;
-		else if (!req->rq_received)
+		else if (!req->rq_received && rpc_reply_expected(task)) {
+			/*
+			 * Sleep on the pending queue since
+			 * we're expecting a reply.
+			 */
 			rpc_sleep_on(&xprt->pending, task, xprt_timer);
+		}
 		spin_unlock_bh(&xprt->transport_lock);
 		return;
 	}
@@ -967,11 +978,15 @@ static void xprt_request_init(struct rpc_task *task, struct rpc_xprt *xprt)
  */
 void xprt_release(struct rpc_task *task)
 {
-	struct rpc_xprt	*xprt = task->tk_xprt;
+	struct rpc_xprt	*xprt;
 	struct rpc_rqst	*req;
+	int prealloc;
 
+	BUG_ON(atomic_read(&task->tk_count) < 0);
 	if (!(req = task->tk_rqstp))
 		return;
+	prealloc = bc_prealloc(req);	/* Preallocated backchannel request? */
+	xprt = req->rq_xprt;
 	rpc_count_iostats(task);
 	spin_lock_bh(&xprt->transport_lock);
 	xprt->ops->release_xprt(xprt, task);
@@ -984,10 +999,19 @@ void xprt_release(struct rpc_task *task)
 		mod_timer(&xprt->timer,
 				xprt->last_used + xprt->idle_timeout);
 	spin_unlock_bh(&xprt->transport_lock);
-	xprt->ops->buf_free(req->rq_buffer);
+	if (!bc_prealloc(req))
+		xprt->ops->buf_free(req->rq_buffer);
 	task->tk_rqstp = NULL;
 	if (req->rq_release_snd_buf)
 		req->rq_release_snd_buf(req);
+
+	/*
+	 * Early exit if this is a backchannel preallocated request.
+	 * There is no need to have it added to the RPC slot list.
+	 */
+	if (prealloc)
+		return;
+
 	memset(req, 0, sizeof(*req));	/* mark unused */
 
 	dprintk("RPC: %5u release request %p\n", task->tk_pid, req);
#87 lore raw

RFC 62/85 nfs41: Backchannel callback service helper routines

In reply to Benny Halevy

From: Ricardo Labiaga <[email protected]>

Executes the backchannel task on the RPC state machine using the existing open connection previously established by the client.

nfs41: Add bc_svc.o to sunrpc Makefile.

---
 include/linux/sunrpc/bc_xprt.h |    3 +
 net/sunrpc/Makefile            |    2 +-
 net/sunrpc/bc_svc.c            |   81 ++++++++++++++++++++++++++++++++++++++++
 net/sunrpc/xprtsock.c          |    3 +
 4 files changed, 88 insertions(+), 1 deletions(-)
 create mode 100644 net/sunrpc/bc_svc.c
include/linux/sunrpc/bc_xprt.h +3 -0
index 8ad9337..9dbeb77 100644
@@ -29,12 +29,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <linux/sunrpc/svcsock.h>
 #include <linux/sunrpc/xprt.h>
+#include <linux/sunrpc/sched.h>
 
 #ifdef CONFIG_NFS_V4_1
 struct rpc_rqst *xprt_alloc_bc_request(struct rpc_xprt *xprt);
 void xprt_free_bc_request(struct rpc_rqst *req);
 int xprt_setup_backchannel(struct rpc_xprt *, unsigned int min_reqs);
 void xprt_destroy_backchannel(struct rpc_xprt *, int max_reqs);
+void bc_release_request(struct rpc_task *);
+int bc_send(struct rpc_rqst *req);
 #endif /* CONFIG_NFS_V4_1 */
 #endif /* _LINUX_SUNRPC_BC_XPRT_H */
 
net/sunrpc/Makefile +1 -1
index 4a01f96..db73fd2 100644
@@ -13,6 +13,6 @@ sunrpc-y := clnt.o xprt.o socklib.o xprtsock.o sched.o \
 	    rpcb_clnt.o timer.o xdr.o \
 	    sunrpc_syms.o cache.o rpc_pipe.o \
 	    svc_xprt.o
-sunrpc-$(CONFIG_NFS_V4_1) += backchannel_rqst.o
+sunrpc-$(CONFIG_NFS_V4_1) += backchannel_rqst.o bc_svc.o
 sunrpc-$(CONFIG_PROC_FS) += stats.o
 sunrpc-$(CONFIG_SYSCTL) += sysctl.o
net/sunrpc/bc_svc.c new file +81 -0

81 lines not on this page · raw message

net/sunrpc/xprtsock.c +3 -0
index 6ccdec8..8513d50 100644
@@ -2046,6 +2046,9 @@ static struct rpc_xprt_ops xs_tcp_ops = {
 	.buf_free		= rpc_free,
 	.send_request		= xs_tcp_send_request,
 	.set_retrans_timeout	= xprt_set_retrans_timeout_def,
+#if defined(CONFIG_NFS_V4_1)
+	.release_request	= bc_release_request,
+#endif /* CONFIG_NFS_V4_1 */
 	.close			= xs_tcp_shutdown,
 	.destroy		= xs_destroy,
 	.print_stats		= xs_tcp_print_stats,
Signature · 1 line
1.6.0.2
#88 lore raw

RFC 63/85 FIXME: nfs41: sunrpc: handle clnt==NULL in call_status

In reply to Benny Halevy

FIXME: pending discussion. This patch might not be needed if backchannel usage of rpc mechanisms for sending bc_svc replies (on the nfs client / callback svc side)

On the following error path task->tk_client == NULL causing NULL deref:

Nov  4 14:14:32 tl1 kernel: Callback slot table overflowed
Nov  4 14:14:32 tl1 kernel: BUG: unable to handle kernel NULL pointer dereference at 0000000000000078
Nov  4 14:14:32 tl1 kernel: IP: [<ffffffffa02b4dd2>] call_status+0xe1/0x145 [sunrpc]
Nov  4 14:14:32 tl1 kernel: PGD 2f973067 PUD 2f974067 PMD 0
Nov  4 14:14:32 tl1 kernel: Oops: 0000 [1] SMP
Nov  4 14:14:32 tl1 kernel: CPU 0
Nov  4 14:14:32 tl1 kernel: Modules linked in: panfs(P) panlayoutdriver nfslayoutdriver nfsd auth_rpcgss exportfs nfs lockd nfs_acl fuse sunrpc ipv6 cpufreq_ondemand powernow_k8 freq_table dm_mirror dm_log dm_multipath dm_mod snd_hda_intel snd_seq_dummy sr_mod snd_seq_oss cdrom snd_seq_midi_event snd_seq snd_seq_device snd_pcm_oss snd_mixer_oss snd_pcm snd_timer snd_page_alloc snd_hwdep snd pcspkr serio_raw k8temp hwmon forcedeth soundcore pata_amd sg jedec_probe cfi_probe gen_probe mtd i2c_nforce2 chipreg i2c_core button map_funcs sata_nv ata_generic pata_acpi libata sd_mod scsi_mod ext3 jbd mbcache uhci_hcd ohci_hcd ehci_hcd [last unloaded: scsi_wait_scan]
Nov  4 14:14:32 tl1 kernel: Pid: 3405, comm: nfsv4.1-svc Tainted: P          2.6.27-pnfs #1
Nov  4 14:14:32 tl1 kernel: RIP: 0010:[<ffffffffa02b4dd2>]  [<ffffffffa02b4dd2>] call_status+0xe1/0x145 [sunrpc]
Nov  4 14:14:32 tl1 kernel: RSP: 0018:ffff88002a70bd60  EFLAGS: 00010246
Nov  4 14:14:32 tl1 kernel: RAX: 0000000000000000 RBX: 00000000ffffff95 RCX: 0000000000000000
Nov  4 14:14:32 tl1 kernel: RDX: ffff88002a70bd10 RSI: ffff88002fc52dc0 RDI: ffff88002fc52dc0
Nov  4 14:14:32 tl1 kernel: RBP: ffff88002a70bd80 R08: ffff88002f9dae40 R09: ffffffff81489e60
Nov  4 14:14:32 tl1 kernel: R10: ffff880038497800 R11: ffff88002a70bd30 R12: ffff88002fc52dc0
Nov  4 14:14:32 tl1 kernel: R13: 0000000000000000 R14: ffff88002f57fa00 R15: ffff88002fc52e50
Nov  4 14:14:32 tl1 kernel: FS:  00007fe78ffac6f0(0000) GS:ffffffff8148da80(0000) knlGS:0000000000000000
Nov  4 14:14:32 tl1 kernel: CS:  0010 DS: 0018 ES: 0018 CR0: 000000008005003b
Nov  4 14:14:32 tl1 kernel: CR2: 0000000000000078 CR3: 000000002f971000 CR4: 00000000000006e0
Nov  4 14:14:32 tl1 kernel: DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
Nov  4 14:14:32 tl1 kernel: DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
Nov  4 14:14:32 tl1 kernel: Process nfsv4.1-svc (pid: 3405, threadinfo ffff88002a70a000, task ffff88003694d900)
Nov  4 14:14:32 tl1 kernel: Stack:  0000000000000000 ffff88002f57fa00 ffff88002fc52dc0 ffff880030450ce8
Nov  4 14:14:32 tl1 kernel: ffff88002a70bdc0 ffffffffa02bb9db ffff88002a70bdf0 ffff88002fc52dc0
Nov  4 14:14:32 tl1 kernel: ffff88002f57fa00 ffff88002f57fa00 ffff880030450ce8 ffff88002a734000
Nov  4 14:14:32 tl1 kernel: Call Trace:
Nov  4 14:14:32 tl1 kernel: [<ffffffffa02bb9db>] __rpc_execute+0x7e/0x21f [sunrpc]
Nov  4 14:14:32 tl1 kernel: [<ffffffffa02bbba4>] rpc_execute+0x28/0x2c [sunrpc]
Nov  4 14:14:32 tl1 kernel: [<ffffffffa02b5596>] rpc_run_bc_task+0x88/0xae [sunrpc]
Nov  4 14:14:32 tl1 kernel: [<ffffffffa02c923d>] bc_send+0x35/0x5a [sunrpc]
Nov  4 14:14:32 tl1 kernel: [<ffffffffa02bdc92>] bc_svc_process+0xf6/0x101 [sunrpc]
Nov  4 14:14:32 tl1 kernel: [<ffffffff81048a6c>] ? prepare_to_wait+0x5c/0x65
Nov  4 14:14:32 tl1 kernel: [<ffffffffa033a4c9>] nfs41_callback_svc+0xda/0x131 [nfs]
Nov  4 14:14:32 tl1 kernel: [<ffffffff810488a0>] ? autoremove_wake_function+0x0/0x38
Nov  4 14:14:32 tl1 kernel: [<ffffffffa033a3ef>] ? nfs41_callback_svc+0x0/0x131 [nfs]
Nov  4 14:14:32 tl1 kernel: [<ffffffff81048561>] kthread+0x49/0x76
Nov  4 14:14:32 tl1 kernel: [<ffffffff8100cf49>] child_rip+0xa/0x11
Nov  4 14:14:32 tl1 kernel: [<ffffffff8102a401>] ? pick_next_task_fair+0x93/0xa3
Nov  4 14:14:32 tl1 kernel: [<ffffffff81048518>] ? kthread+0x0/0x76
Nov  4 14:14:32 tl1 kernel: [<ffffffff8100cf3f>] ? child_rip+0x0/0x11
Nov  4 14:14:32 tl1 kernel:
Nov  4 14:14:32 tl1 kernel:
Nov  4 14:14:32 tl1 kernel: Code: e8 7c 70 00 00 49 8b 44 24 20 49 c7 44 24 68 dc 48 2b a0 f6 40 78 02 74 6d 41 8b b6 34 01 00 00 48 8b 78 30 e8 b9 2a 00 00 eb 5b <41> f6 45 78 04 74 13 49 8b 55 30 48 8d 82 80 04 00 00 f0 80 a2
Nov  4 14:14:32 tl1 kernel: RIP  [<ffffffffa02b4dd2>] call_status+0xe1/0x145 [sunrpc]
Nov  4 14:14:32 tl1 kernel: RSP <ffff88002a70bd60>
Nov  4 14:14:32 tl1 kernel: CR2: 0000000000000078
Nov  4 14:14:32 tl1 kernel: ---[ end trace 1584c489f2573aaf ]---
---
 net/sunrpc/clnt.c |    8 +++++---
 1 files changed, 5 insertions(+), 3 deletions(-)
net/sunrpc/clnt.c +5 -3
index f5418ac..fcadd8e 100644
@@ -1204,7 +1204,8 @@ call_status(struct rpc_task *task)
 		break;
 	case -ECONNREFUSED:
 	case -ENOTCONN:
-		rpc_force_rebind(clnt);
+		if (clnt)
+			rpc_force_rebind(clnt);
 		task->tk_action = call_bind;
 		break;
 	case -EAGAIN:
@@ -1215,9 +1216,10 @@ call_status(struct rpc_task *task)
 		rpc_exit(task, status);
 		break;
 	default:
-		if (clnt->cl_chatty)
+		if (!clnt || clnt->cl_chatty)
 			printk("%s: RPC call returned error %d\n",
-			       clnt->cl_protname, -status);
+			       clnt ? clnt->cl_protname : "<unknown protocol>",
+			       -status);
 		rpc_exit(task, status);
 	}
 }
Signature · 1 line
1.6.0.2
#89 lore raw

RFC 64/85 nfs41: Refactor svc_process()

In reply to Benny Halevy

From: Ricardo Labiaga <[email protected]>

net/sunrpc/svc.c:svc_process() is used by the NFSv4 callback service to process RPC requests arriving over connections initiated by the server. NFSv4.1 supports callbacks over the backchannel on connections initiated by the client. This patch refactors svc_process() so that common code can also be used by the backchannel.

---
 fs/nfs/callback.c |   10 +++---
 net/sunrpc/svc.c  |   80 +++++++++++++++++++++++++++++++++--------------------
 2 files changed, 55 insertions(+), 35 deletions(-)
fs/nfs/callback.c +5 -5
index e792706..2f5acd3 100644
@@ -132,7 +132,7 @@ nfs4_callback_up(struct svc_serv *serv)
 int nfs_callback_up(u32 minorversion, void *args)
 {
 	struct svc_serv *serv = NULL;
-	struct svc_rqst *rqstp;
+	struct svc_rqst *rqst;
 	int (* callback_svc)(void *vrqstp);
 	char svc_name[12];
 	int ret = 0;
@@ -150,20 +150,20 @@ int nfs_callback_up(u32 minorversion, void *args)
 	/* FIXME: either 4.0 or 4.1 callback service can be up at a time
 	 * need to monitor and control them both */
 	if (!minorversion) {
-		rqstp = nfs4_callback_up(serv);
+		rqst = nfs4_callback_up(serv);
 		callback_svc = nfs4_callback_svc;
 	} else {
 		BUG();	/* for now */
 	}
-	if (IS_ERR(rqstp)) {
-		ret = PTR_ERR(rqstp);
+	if (IS_ERR(rqst)) {
+		ret = PTR_ERR(rqst);
 		goto out_err;
 	}
 
 	svc_sock_update_bufs(serv);
 
 	sprintf(svc_name, "nfsv4.%u-svc", minorversion);
-	nfs_callback_info.rqst = rqstp;
+	nfs_callback_info.rqst = rqst;
 	nfs_callback_info.task = kthread_run(callback_svc,
 					     nfs_callback_info.rqst,
 					     svc_name);
net/sunrpc/svc.c +50 -30

131 lines not on this page · raw message

Signature · 1 line
1.6.0.2
#90 lore raw

Re: [pnfs] [RFC 64/85] nfs41: Refactor svc_process()

Quoted 58 lines · Benny Halevy

From: Ricardo Labiaga <[email protected]>

net/sunrpc/svc.c:svc_process() is used by the NFSv4 callback service to process RPC requests arriving over connections initiated by the server. NFSv4.1 supports callbacks over the backchannel on connections initiated by the client. This patch refactors svc_process() so that common code can also be used by the backchannel.

---
 fs/nfs/callback.c |   10 +++---
 net/sunrpc/svc.c  |   80 +++++++++++++++++++++++++++++++++--------------------
 2 files changed, 55 insertions(+), 35 deletions(-)
fs/nfs/callback.c +5 -5
index e792706..2f5acd3 100644
@@ -132,7 +132,7 @@ nfs4_callback_up(struct svc_serv *serv)
 int nfs_callback_up(u32 minorversion, void *args)
 {
 	struct svc_serv *serv = NULL;
-	struct svc_rqst *rqstp;
+	struct svc_rqst *rqst;
 	int (* callback_svc)(void *vrqstp);
 	char svc_name[12];
 	int ret = 0;
@@ -150,20 +150,20 @@ int nfs_callback_up(u32 minorversion, void *args)
 	/* FIXME: either 4.0 or 4.1 callback service can be up at a time
 	 * need to monitor and control them both */
 	if (!minorversion) {
-		rqstp = nfs4_callback_up(serv);
+		rqst = nfs4_callback_up(serv);
 		callback_svc = nfs4_callback_svc;
 	} else {
 		BUG();	/* for now */
 	}
-	if (IS_ERR(rqstp)) {
-		ret = PTR_ERR(rqstp);
+	if (IS_ERR(rqst)) {
+		ret = PTR_ERR(rqst);
 		goto out_err;
 	}
 
 	svc_sock_update_bufs(serv);
 
 	sprintf(svc_name, "nfsv4.%u-svc", minorversion);
-	nfs_callback_info.rqst = rqstp;
+	nfs_callback_info.rqst = rqst;
 	nfs_callback_info.task = kthread_run(callback_svc,
 					     nfs_callback_info.rqst,
 					     svc_name);
net/sunrpc/svc.c +0 -0
index 54c98d8..e4efeeb 100644

net/sunrpc/svc.c svc_printk(struct svc_rqst *rqstp, const char *fmt, ...) · line 991

Quoted 10 lines
net/sunrpc/svc.c +3 -3
@@ -985,20 +985,18 @@ svc_printk(struct svc_rqst *rqstp, const char *fmt, ...)
 }
 
 /*
- * Process the RPC request.
+ * Common routine for processing the RPC request.
  */
-int
-svc_process(struct svc_rqst *rqstp)
+static int
+svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)

review 11-14: we have a problem with authenticating here over gss. to support gss we need to factor out (everything but authentication?) we need to keep the header part and processing part.

Quoted 125 lines
net/sunrpc/svc.c +47 -27
 {
 	struct svc_program	*progp;
 	struct svc_version	*versp = NULL;	/* compiler food */
 	struct svc_procedure	*procp = NULL;
-	struct kvec *		argv = &rqstp->rq_arg.head[0];
-	struct kvec *		resv = &rqstp->rq_res.head[0];
 	struct svc_serv		*serv = rqstp->rq_server;
 	kxdrproc_t		xdr;
 	__be32			*statp;
-	u32			dir, prog, vers, proc;
+	u32			prog, vers, proc;
 	__be32			auth_stat, rpc_stat;
 	int			auth_res;
 	__be32			*reply_statp;
@@ -1008,36 +1006,19 @@ svc_process(struct svc_rqst *rqstp)
 	if (argv->iov_len < 6*4)
 		goto err_short_len;
 
-	/* setup response xdr_buf.
-	 * Initially it has just one page
-	 */
-	rqstp->rq_resused = 1;
-	resv->iov_base = page_address(rqstp->rq_respages[0]);
-	resv->iov_len = 0;
-	rqstp->rq_res.pages = rqstp->rq_respages + 1;
-	rqstp->rq_res.len = 0;
-	rqstp->rq_res.page_base = 0;
-	rqstp->rq_res.page_len = 0;
-	rqstp->rq_res.buflen = PAGE_SIZE;
-	rqstp->rq_res.tail[0].iov_base = NULL;
-	rqstp->rq_res.tail[0].iov_len = 0;
 	/* Will be turned off only in gss privacy case: */
 	rqstp->rq_splice_ok = 1;
 
 	/* Setup reply header */
 	rqstp->rq_xprt->xpt_ops->xpo_prep_reply_hdr(rqstp);
 
-	rqstp->rq_xid = svc_getu32(argv);
 	svc_putu32(resv, rqstp->rq_xid);
 
-	dir  = svc_getnl(argv);
 	vers = svc_getnl(argv);
 
 	/* First words of reply: */
 	svc_putnl(resv, 1);		/* REPLY */
 
-	if (dir != 0)		/* direction != CALL */
-		goto err_bad_dir;
 	if (vers != 2)		/* RPC version number */
 		goto err_bad_rpc;
 
@@ -1161,7 +1142,7 @@ svc_process(struct svc_rqst *rqstp)
  sendit:
 	if (svc_authorise(rqstp))
 		goto dropit;
-	return svc_send(rqstp);
+	return 1;		/* Caller can now send it */
 
  dropit:
 	svc_authorise(rqstp);	/* doesn't hurt to call this twice */
@@ -1175,12 +1156,6 @@ err_short_len:
 
 	goto dropit;			/* drop request */
 
-err_bad_dir:
-	svc_printk(rqstp, "bad direction %d, dropping request\n", dir);
-
-	serv->sv_stats->rpcbadfmt++;
-	goto dropit;			/* drop request */
-
 err_bad_rpc:
 	serv->sv_stats->rpcbadfmt++;
 	svc_putnl(resv, 1);	/* REJECT */
@@ -1234,6 +1209,51 @@ err_bad:
 EXPORT_SYMBOL(svc_process);
 
 /*
+ * Process the RPC request.
+ */
+int
+svc_process(struct svc_rqst *rqstp)
+{
+	struct kvec		*argv = &rqstp->rq_arg.head[0];
+	struct kvec		*resv = &rqstp->rq_res.head[0];
+	struct svc_serv		*serv = rqstp->rq_server;
+	u32			dir;
+	int			error;
+
+	/*
+	 * Setup response xdr_buf.
+	 * Initially it has just one page
+	 */
+	rqstp->rq_resused = 1;
+	resv->iov_base = page_address(rqstp->rq_respages[0]);
+	resv->iov_len = 0;
+	rqstp->rq_res.pages = rqstp->rq_respages + 1;
+	rqstp->rq_res.len = 0;
+	rqstp->rq_res.page_base = 0;
+	rqstp->rq_res.page_len = 0;
+	rqstp->rq_res.buflen = PAGE_SIZE;
+	rqstp->rq_res.tail[0].iov_base = NULL;
+	rqstp->rq_res.tail[0].iov_len = 0;
+
+	rqstp->rq_xid = svc_getu32(argv);
+
+	dir  = svc_getnl(argv);
+	if (dir != 0) {
+		/* direction != CALL */
+		svc_printk(rqstp, "bad direction %d, dropping request\n", dir);
+		serv->sv_stats->rpcbadfmt++;
+		svc_drop(rqstp);
+		return 0;
+	}
+
+	error = svc_process_common(rqstp, argv, resv);
+	if (error <= 0)
+		return error;
+
+	return svc_send(rqstp);
+}
+
+/*
  * Return (transport-specific) limit on the rpc payload.
  */
 u32 svc_max_payload(const struct svc_rqst *rqstp)
#91 lore raw

RFC 65/85 nfs41: Backchannel bc_svc_process()

In reply to Benny Halevy

From: Ricardo Labiaga <[email protected]>

Implement the NFSv4.1 backchannel service. Invokes the common callback processing logic svc_process_common() to authenticate the call and dispatch the appropriate NFSv4.1 XDR decoder and operation procedure. It then invokes bc_send() to send the reply over the same connection. bc_send() is implemented in a separate patch.

At this time there is no slot validation or reply cache handling.

---
 net/sunrpc/svc.c |   49 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 49 insertions(+), 0 deletions(-)
net/sunrpc/svc.c +49 -0
index e4efeeb..906b992 100644
@@ -25,6 +25,7 @@
 #include <linux/sunrpc/stats.h>
 #include <linux/sunrpc/svcsock.h>
 #include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/bc_xprt.h>
 
 #define RPCDBG_FACILITY	RPCDBG_SVCDSP
 
@@ -1253,6 +1254,54 @@ svc_process(struct svc_rqst *rqstp)
 	return svc_send(rqstp);
 }
 
+#if defined(CONFIG_NFS_V4_1)
+/*
+ * Process a backchannel RPC request that arrived over an existing
+ * outbound connection
+ */
+int
+bc_svc_process(struct svc_serv *serv, struct rpc_rqst *req,
+	       struct svc_rqst *rqstp)
+{
+	struct kvec	*argv = &rqstp->rq_arg.head[0];
+	struct kvec	*resv = &rqstp->rq_res.head[0];
+	int 		error;
+
+	/* Build the svc_rqst used by the common processing routine */
+	rqstp->rq_xid = req->rq_xid;
+	rqstp->rq_prot = req->rq_xprt->prot;
+	rqstp->rq_server = serv;
+
+	rqstp->rq_addrlen = sizeof(req->rq_xprt->addr);
+	memcpy(&rqstp->rq_addr, &req->rq_xprt->addr, rqstp->rq_addrlen);
+	memcpy(&rqstp->rq_arg, &req->rq_private_buf, sizeof(rqstp->rq_arg));
+	memcpy(&rqstp->rq_res, &req->rq_snd_buf, sizeof(rqstp->rq_res));
+
+	/* reset result send buffer "put" position */
+	resv->iov_len = 0;
+
+	if (rqstp->rq_prot != IPPROTO_TCP) {
+		printk(KERN_ERR "No support for Non-TCP transports!\n");
+		BUG();
+	}
+
+	/*
+	 * Skip the next two words because they've already been
+	 * processed in the trasport
+	 */
+	svc_getu32(argv);	/* XID */
+	svc_getnl(argv);	/* CALLDIR */
+
+	error = svc_process_common(rqstp, argv, resv);
+	if (error <= 0)
+		return error;
+
+	memcpy(&req->rq_snd_buf, &rqstp->rq_res, sizeof(req->rq_snd_buf));
+	return bc_send(req);
+}
+EXPORT_SYMBOL(bc_svc_process);
+#endif /* CONFIG_NFS_V4_1 */
+
 /*
  * Return (transport-specific) limit on the rpc payload.
  */
Signature · 1 line
1.6.0.2
#92 lore raw

Re: [pnfs] [RFC 65/85] nfs41: Backchannel bc_svc_process()

Quoted 65 lines · Benny Halevy

From: Ricardo Labiaga <[email protected]>

Implement the NFSv4.1 backchannel service. Invokes the common callback processing logic svc_process_common() to authenticate the call and dispatch the appropriate NFSv4.1 XDR decoder and operation procedure. It then invokes bc_send() to send the reply over the same connection. bc_send() is implemented in a separate patch.

At this time there is no slot validation or reply cache handling.

---
 net/sunrpc/svc.c |   49 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 49 insertions(+), 0 deletions(-)
net/sunrpc/svc.c +34 -0
index e4efeeb..906b992 100644
@@ -25,6 +25,7 @@
 #include <linux/sunrpc/stats.h>
 #include <linux/sunrpc/svcsock.h>
 #include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/bc_xprt.h>
 
 #define RPCDBG_FACILITY	RPCDBG_SVCDSP
 
@@ -1253,6 +1254,54 @@ svc_process(struct svc_rqst *rqstp)
 	return svc_send(rqstp);
 }
 
+#if defined(CONFIG_NFS_V4_1)
+/*
+ * Process a backchannel RPC request that arrived over an existing
+ * outbound connection
+ */
+int
+bc_svc_process(struct svc_serv *serv, struct rpc_rqst *req,
+	       struct svc_rqst *rqstp)
+{
+	struct kvec	*argv = &rqstp->rq_arg.head[0];
+	struct kvec	*resv = &rqstp->rq_res.head[0];
+	int 		error;
+
+	/* Build the svc_rqst used by the common processing routine */
+	rqstp->rq_xid = req->rq_xid;
+	rqstp->rq_prot = req->rq_xprt->prot;
+	rqstp->rq_server = serv;
+
+	rqstp->rq_addrlen = sizeof(req->rq_xprt->addr);
+	memcpy(&rqstp->rq_addr, &req->rq_xprt->addr, rqstp->rq_addrlen);
+	memcpy(&rqstp->rq_arg, &req->rq_private_buf, sizeof(rqstp->rq_arg));
+	memcpy(&rqstp->rq_res, &req->rq_snd_buf, sizeof(rqstp->rq_res));
+
+	/* reset result send buffer "put" position */
+	resv->iov_len = 0;
+
+	if (rqstp->rq_prot != IPPROTO_TCP) {
+		printk(KERN_ERR "No support for Non-TCP transports!\n");
+		BUG();
+	}
+
+	/*
+	 * Skip the next two words because they've already been

net/sunrpc/svc.c svc_process(struct svc_rqst *rqstp) · line 1299

Quoted 10 lines
net/sunrpc/svc.c +10 -0
+	 * processed in the trasport
+	 */
+	svc_getu32(argv);	/* XID */
+	svc_getnl(argv);	/* CALLDIR */
+
+	error = svc_process_common(rqstp, argv, resv);
+	if (error <= 0)
+		return error;
+
+	memcpy(&req->rq_snd_buf, &rqstp->rq_res, sizeof(req->rq_snd_buf));

review 11-14: just copy the pointers, not data.

Quoted 8 lines
net/sunrpc/svc.c +5 -0
+	return bc_send(req);
+}
+EXPORT_SYMBOL(bc_svc_process);
+#endif /* CONFIG_NFS_V4_1 */
+
 /*
  * Return (transport-specific) limit on the rpc payload.
  */
#93 lore raw

RFC 66/85 nfs41: Implement NFSv4.1 callback service process.

In reply to Benny Halevy

From: Ricardo Labiaga <[email protected]>

nfs41_callback_up() initializes the necessary queues and creates the new nfs41_callback_svc thread. This thread executes the callback service which waits for requests to arrive on the svc_serv->sv_cb_list.

NFS41_BC_MIN_CALLBACKS is set to 1 because we expect callbacks to not cause substantial latency.

The actual processing of the callback will be implemented as a separate patch.

---
 fs/nfs/callback.c          |   76 +++++++++++++++++++++++++++++++++++++++++++-
 fs/nfs/callback.h          |    8 +++++
 include/linux/sunrpc/svc.h |    2 +
 3 files changed, 85 insertions(+), 1 deletions(-)
fs/nfs/callback.c +75 -1
index 2f5acd3..86f4d88 100644
@@ -16,6 +16,9 @@
 #include <linux/mutex.h>
 #include <linux/freezer.h>
 #include <linux/kthread.h>
+#if defined(CONFIG_NFS_V4_1)
+#include <linux/sunrpc/bc_xprt.h>
+#endif
 
 #include <net/inet_sock.h>
 
@@ -126,6 +129,69 @@ nfs4_callback_up(struct svc_serv *serv)
 	return svc_prepare_thread(serv, &serv->sv_pools[0]);
 }
 
+#if defined(CONFIG_NFS_V4_1)
+/*
+ * The callback service for NFSv4.1 callbacks
+ */
+static int
+nfs41_callback_svc(void *vrqstp)
+{
+	struct svc_rqst *rqstp = vrqstp;
+	struct svc_serv *serv = rqstp->rq_server;
+	struct rpc_rqst *req;
+	int error;
+	DEFINE_WAIT(wq);
+
+	set_freezable();
+
+	/*
+	 * FIXME: do we really need to run this under the BKL? If so, please
+	 * add a comment about what it's intended to protect.
+	 */
+	lock_kernel();
+	while (!kthread_should_stop()) {
+		prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE);
+		spin_lock_bh(&serv->sv_cb_lock);
+		if (!list_empty(&serv->sv_cb_list)) {
+			req = list_first_entry(&serv->sv_cb_list,
+					struct rpc_rqst, rq_bc_list);
+			list_del(&req->rq_bc_list);
+			spin_unlock_bh(&serv->sv_cb_lock);
+			dprintk("Invoking bc_svc_process()\n");
+			error = bc_svc_process(serv, req, rqstp);
+			dprintk("bc_svc_process() returned w/ error code= %d\n",
+				error);
+		} else {
+			spin_unlock_bh(&serv->sv_cb_lock);
+			schedule();
+		}
+		finish_wait(&serv->sv_cb_waitq, &wq);
+	}
+	unlock_kernel();
+	nfs_callback_info.task = NULL;
+	svc_exit_thread(rqstp);
+	return 0;
+}
+
+/*
+ * Bring up the NFSv4.1 callback service
+ */
+struct svc_rqst *
+nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt)
+{
+	/*
+	 * Save the svc_serv in the transport so that it can
+	 * be referenced when the session backchannel is initialized
+	 */
+	xprt->bc_serv = serv;
+
+	INIT_LIST_HEAD(&serv->sv_cb_list);
+	spin_lock_init(&serv->sv_cb_lock);
+	init_waitqueue_head(&serv->sv_cb_waitq);
+	return svc_prepare_thread(serv, &serv->sv_pools[0]);
+}
+#endif /* CONFIG_NFS_V4_1 */
+
 /*
  * Bring up the callback thread if it is not already up.
  */
@@ -136,6 +202,9 @@ int nfs_callback_up(u32 minorversion, void *args)
 	int (* callback_svc)(void *vrqstp);
 	char svc_name[12];
 	int ret = 0;
+#if defined(CONFIG_NFS_V4_1)
+	struct rpc_xprt *xprt = (struct rpc_xprt *)args;
+#endif /* CONFIG_NFS_V4_1 */
 
 	mutex_lock(&nfs_callback_mutex);
 	if (nfs_callback_info.users++ || nfs_callback_info.task != NULL)
@@ -153,7 +222,12 @@ int nfs_callback_up(u32 minorversion, void *args)
 		rqst = nfs4_callback_up(serv);
 		callback_svc = nfs4_callback_svc;
 	} else {
-		BUG();	/* for now */
+#if defined(CONFIG_NFS_V4_1)
+		rqst = nfs41_callback_up(serv, xprt);
+		callback_svc = nfs41_callback_svc;
+#else  /* CONFIG_NFS_V4_1 */
+		BUG();
+#endif /* CONFIG_NFS_V4_1 */
 	}
 	if (IS_ERR(rqst)) {
 		ret = PTR_ERR(rqst);
fs/nfs/callback.h +8 -0
index a2b3ce4..49ea368 100644
@@ -70,6 +70,14 @@ extern void nfs_callback_down(void);
 #define nfs_callback_down()	do {} while(0)
 #endif
 
+#ifdef CONFIG_NFS_V4_1
+/*
+ * Callbacks are expected to not cause substantial latency,
+ * so we limit their concurrency to 1.
+ */
+#define NFS41_BC_MIN_CALLBACKS 1
+#endif /* CONFIG_NFS_V4_1 */
+
 extern unsigned int nfs_callback_set_tcpport;
 extern unsigned short nfs_callback_tcpport;
 
include/linux/sunrpc/svc.h +2 -0
index 1d3b971..1bb51dc 100644
@@ -403,6 +403,8 @@ struct svc_serv *  svc_create_pooled(struct svc_program *, unsigned int,
 int		   svc_set_num_threads(struct svc_serv *, struct svc_pool *, int);
 void		   svc_destroy(struct svc_serv *);
 int		   svc_process(struct svc_rqst *);
+int		   bc_svc_process(struct svc_serv *, struct rpc_rqst *,
+			struct svc_rqst *);
 int		   svc_register(const struct svc_serv *, const unsigned short,
 				const unsigned short);
 
Signature · 1 line
1.6.0.2
#94 lore raw

RFC 67/85 nfs41: sunrpc: provide functions to create and destroy a svc_xprt for backchannel use

In reply to Benny Halevy

For nfs41 callbacks we need an svc_xprt to process requests coming up the backchannel socket as rpc_rqst's that are transformed into svc_rqst's that need a rq_xprt to be processed.

The svc_{udp,tcp}_create methods are too heavy for this job as svc_create_socket creates an actual socket to listen on while for nfs41 we're "reusing" the fore channel's socket.

---
 include/linux/sunrpc/svcsock.h |    2 ++
 net/sunrpc/svcsock.c           |   39 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 41 insertions(+), 0 deletions(-)
include/linux/sunrpc/svcsock.h +2 -0
index 483e103..6bb1ec4 100644
@@ -42,6 +42,8 @@ int		svc_sock_names(char *buf, struct svc_serv *serv, char *toclose);
 int		svc_addsock(struct svc_serv *serv, int fd, char *name_return);
 void		svc_init_xprt_sock(void);
 void		svc_cleanup_xprt_sock(void);
+struct svc_xprt *svc_sock_create(struct svc_serv *serv, int prot);
+void		svc_sock_destroy(struct svc_xprt *);
 
 /*
  * svc_makesock socket characteristics
net/sunrpc/svcsock.c +39 -0
index 95293f5..c2b6fa4 100644
@@ -1298,3 +1298,42 @@ static void svc_sock_free(struct svc_xprt *xprt)
 		sock_release(svsk->sk_sock);
 	kfree(svsk);
 }
+
+/*
+ * Create a svc_xprt.
+ *
+ * For internal use only (e.g. nfsv4.1 backchannel).
+ * Callers should typically use the xpo_create() method.
+ */
+struct svc_xprt *svc_sock_create(struct svc_serv *serv, int prot)
+{
+	struct svc_sock *svsk;
+	struct svc_xprt *xprt = NULL;
+
+	dprintk("svc: %s\n", __func__);
+	svsk = kzalloc(sizeof(*svsk), GFP_KERNEL);
+	if (!svsk)
+		goto out;
+
+	xprt = &svsk->sk_xprt;
+	if (prot == IPPROTO_TCP)
+		svc_xprt_init(&svc_tcp_class, xprt, serv);
+	else if (prot == IPPROTO_UDP)
+		svc_xprt_init(&svc_udp_class, xprt, serv);
+	else
+		BUG();
+out:
+	dprintk("svc: %s return %p\n", __func__, xprt);
+	return xprt;
+}
+EXPORT_SYMBOL_GPL(svc_sock_create);
+
+/*
+ * Destroy a svc_sock.
+ */
+void svc_sock_destroy(struct svc_xprt *xprt)
+{
+	if (xprt)
+		kfree(container_of(xprt, struct svc_sock, sk_xprt));
+}
+EXPORT_SYMBOL_GPL(svc_sock_destroy);
Signature · 1 line
1.6.0.2
#95 lore raw

RFC 68/85 nfs41: sunrpc: add a struct svc_xprt pointer to struct svc_serv for backchannel use

In reply to Benny Halevy

This svc_xprt is passed on to the callback service thread to be later used to processes incoming svc_rqst's

---
 include/linux/sunrpc/svc.h |    1 +
 net/sunrpc/svc.c           |    4 ++++
 2 files changed, 5 insertions(+), 0 deletions(-)
include/linux/sunrpc/svc.h +1 -0
index 1bb51dc..7653dd3 100644
@@ -90,6 +90,7 @@ struct svc_serv {
 	wait_queue_head_t	sv_cb_waitq;	/* sleep here if there are no
 						 * entries in the svc_cb_list
 						 */
+	struct svc_xprt		*bc_xprt;
 #endif /* CONFIG_NFS_V4_1 */
 };
 
net/sunrpc/svc.c +4 -0
index 906b992..b84ae54 100644
@@ -489,6 +489,10 @@ svc_destroy(struct svc_serv *serv)
 	if (svc_serv_is_pooled(serv))
 		svc_pool_map_put();
 
+#if defined(CONFIG_NFS_V4_1)
+	svc_sock_destroy(serv->bc_xprt);
+#endif /* CONFIG_NFS_V4_1 */
+
 	svc_unregister(serv);
 	kfree(serv->sv_pools);
 	kfree(serv);
Signature · 1 line
1.6.0.2
#96 lore raw

RFC 69/85 nfs41: create a svc_xprt for nfs41 callback thread and use for incoming callbacks

In reply to Benny Halevy
---
 fs/nfs/callback.c |   17 ++++++++++++++++-
 net/sunrpc/svc.c  |    1 +
 2 files changed, 17 insertions(+), 1 deletions(-)
fs/nfs/callback.c +16 -1
index 86f4d88..fc30f87 100644
@@ -179,16 +179,31 @@ nfs41_callback_svc(void *vrqstp)
 struct svc_rqst *
 nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt)
 {
+	struct svc_xprt *bc_xprt;
+	struct svc_rqst *rqstp = ERR_PTR(-ENOMEM);
+
+	dprintk("--> %s\n", __func__);
+	/* Create a svc_sock for the service */
+	bc_xprt = svc_sock_create(serv, xprt->prot);
+	if (!bc_xprt)
+		goto out;
+
 	/*
 	 * Save the svc_serv in the transport so that it can
 	 * be referenced when the session backchannel is initialized
 	 */
+	serv->bc_xprt = bc_xprt;
 	xprt->bc_serv = serv;
 
 	INIT_LIST_HEAD(&serv->sv_cb_list);
 	spin_lock_init(&serv->sv_cb_lock);
 	init_waitqueue_head(&serv->sv_cb_waitq);
-	return svc_prepare_thread(serv, &serv->sv_pools[0]);
+	rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]);
+	if (IS_ERR(rqstp))
+		svc_sock_destroy(bc_xprt);
+out:
+	dprintk("--> %s return %p\n", __func__, rqstp);
+	return rqstp;
 }
 #endif /* CONFIG_NFS_V4_1 */
 
net/sunrpc/svc.c +1 -0
index b84ae54..1b95de7 100644
@@ -1272,6 +1272,7 @@ bc_svc_process(struct svc_serv *serv, struct rpc_rqst *req,
 	int 		error;
 
 	/* Build the svc_rqst used by the common processing routine */
+	rqstp->rq_xprt = serv->bc_xprt;
 	rqstp->rq_xid = req->rq_xid;
 	rqstp->rq_prot = req->rq_xprt->prot;
 	rqstp->rq_server = serv;
Signature · 1 line
1.6.0.2
#97 lore raw

RFC 70/85 nfs41: save svc_serv in nfs_callback_info

In reply to Benny Halevy

We need to pass the svc_serv* to the xprt when nfs_callback_info.users > 0

---
 fs/nfs/callback.c |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)
fs/nfs/callback.c +4 -0
index fc30f87..be8ac50 100644
@@ -30,6 +30,7 @@
 
 struct nfs_callback_data {
 	unsigned int users;
+	struct svc_serv *serv;
 	struct svc_rqst *rqst;
 	struct task_struct *task;
 };
@@ -252,6 +253,7 @@ int nfs_callback_up(u32 minorversion, void *args)
 	svc_sock_update_bufs(serv);
 
 	sprintf(svc_name, "nfsv4.%u-svc", minorversion);
+	nfs_callback_info.serv = serv;
 	nfs_callback_info.rqst = rqst;
 	nfs_callback_info.task = kthread_run(callback_svc,
 					     nfs_callback_info.rqst,
@@ -259,6 +261,7 @@ int nfs_callback_up(u32 minorversion, void *args)
 	if (IS_ERR(nfs_callback_info.task)) {
 		ret = PTR_ERR(nfs_callback_info.task);
 		svc_exit_thread(nfs_callback_info.rqst);
+		nfs_callback_info.serv = NULL;
 		nfs_callback_info.rqst = NULL;
 		nfs_callback_info.task = NULL;
 		goto out_err;
@@ -291,6 +294,7 @@ void nfs_callback_down(void)
 	if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL) {
 		kthread_stop(nfs_callback_info.task);
 		svc_exit_thread(nfs_callback_info.rqst);
+		nfs_callback_info.serv = NULL;
 		nfs_callback_info.rqst = NULL;
 		nfs_callback_info.task = NULL;
 	}
Signature · 1 line
1.6.0.2
#98 lore raw

RFC 71/85 nfs41: Add a reference to svc_serv during callback service bring up

In reply to Benny Halevy

From: Ricardo Labiaga <[email protected]>

There is only one NFSv4.1 callback service. The first caller of nfs4_callback_up() creates the service, subsequent callers increment a reference count on the service. The service is destroyed when the last caller invokes nfs_callback_down().

The transport needs to hold a reference to the callback service in order to invoke it during callback processing. Currently this reference is only obtained when the service is first created. This is incorrect, since subsequent registrations for other transports will leave the xprt->serv pointer uninitialized, leading to an oops when a callback arrives on the "unreferenced" transport.

This patch fixes the problem by ensuring that a reference to the service is saved in xprt->serv, either because the service is created by this invocation to nfs4_callback_up() or by a prior invocation.

---
 fs/nfs/callback.c |    7 ++++++-
 1 files changed, 6 insertions(+), 1 deletions(-)
fs/nfs/callback.c +6 -1
index be8ac50..036d66a 100644
@@ -223,8 +223,13 @@ int nfs_callback_up(u32 minorversion, void *args)
 #endif /* CONFIG_NFS_V4_1 */
 
 	mutex_lock(&nfs_callback_mutex);
-	if (nfs_callback_info.users++ || nfs_callback_info.task != NULL)
+	if (nfs_callback_info.users++ || nfs_callback_info.task != NULL) {
+#if defined(CONFIG_NFS_V4_1)
+		if (minorversion)
+			xprt->bc_serv = nfs_callback_info.serv;
+#endif /* CONFIG_NFS_V4_1 */
 		goto out;
+	}
 	serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE,
 				nfs_callback_family, NULL);
 	if (!serv) {
Signature · 1 line
1.6.0.2
#99 lore raw

RFC 72/85 nfs41: Allow NFSv4 and NFSv4.1 callback services to coexist

In reply to Benny Halevy

From: Ricardo Labiaga <[email protected]>

Tracks the nfs_callback_info for both versions, enabling the callback service for v4 and v4.1 to run concurrently and be stopped independently of each other.

---
 fs/nfs/callback.c |   50 ++++++++++++++++++++++++--------------------------
 fs/nfs/callback.h |    7 ++-----
 fs/nfs/client.c   |    4 +++-
 3 files changed, 29 insertions(+), 32 deletions(-)
fs/nfs/callback.c +24 -26
index 036d66a..ff9ce3e 100644
@@ -35,7 +35,7 @@ struct nfs_callback_data {
 	struct task_struct *task;
 };
 
-static struct nfs_callback_data nfs_callback_info;
+static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1];
 static DEFINE_MUTEX(nfs_callback_mutex);
 static struct svc_program nfs4_callback_program;
 
@@ -68,7 +68,7 @@ module_param_call(callback_tcpport, param_set_port, param_get_int,
 		 &nfs_callback_set_tcpport, 0644);
 
 /*
- * This is the callback kernel thread.
+ * This is the NFSv4 callback kernel thread.
  */
 static int
 nfs4_callback_svc(void *vrqstp)
@@ -169,8 +169,6 @@ nfs41_callback_svc(void *vrqstp)
 		finish_wait(&serv->sv_cb_waitq, &wq);
 	}
 	unlock_kernel();
-	nfs_callback_info.task = NULL;
-	svc_exit_thread(rqstp);
 	return 0;
 }
 
@@ -216,6 +214,7 @@ int nfs_callback_up(u32 minorversion, void *args)
 	struct svc_serv *serv = NULL;
 	struct svc_rqst *rqst;
 	int (* callback_svc)(void *vrqstp);
+	struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
 	char svc_name[12];
 	int ret = 0;
 #if defined(CONFIG_NFS_V4_1)
@@ -223,10 +222,10 @@ int nfs_callback_up(u32 minorversion, void *args)
 #endif /* CONFIG_NFS_V4_1 */
 
 	mutex_lock(&nfs_callback_mutex);
-	if (nfs_callback_info.users++ || nfs_callback_info.task != NULL) {
+	if (cb_info->users++ || cb_info->task != NULL) {
 #if defined(CONFIG_NFS_V4_1)
 		if (minorversion)
-			xprt->bc_serv = nfs_callback_info.serv;
+			xprt->bc_serv = cb_info->serv;
 #endif /* CONFIG_NFS_V4_1 */
 		goto out;
 	}
@@ -258,17 +257,14 @@ int nfs_callback_up(u32 minorversion, void *args)
 	svc_sock_update_bufs(serv);
 
 	sprintf(svc_name, "nfsv4.%u-svc", minorversion);
-	nfs_callback_info.serv = serv;
-	nfs_callback_info.rqst = rqst;
-	nfs_callback_info.task = kthread_run(callback_svc,
-					     nfs_callback_info.rqst,
-					     svc_name);
-	if (IS_ERR(nfs_callback_info.task)) {
-		ret = PTR_ERR(nfs_callback_info.task);
-		svc_exit_thread(nfs_callback_info.rqst);
-		nfs_callback_info.serv = NULL;
-		nfs_callback_info.rqst = NULL;
-		nfs_callback_info.task = NULL;
+	cb_info->serv = serv;
+	cb_info->rqst = rqst;
+	cb_info->task = kthread_run(callback_svc, cb_info->rqst, svc_name);
+	if (IS_ERR(cb_info->task)) {
+		ret = PTR_ERR(cb_info->task);
+		svc_exit_thread(cb_info->rqst);
+		cb_info->rqst = NULL;
+		cb_info->task = NULL;
 		goto out_err;
 	}
 out:
@@ -285,23 +281,25 @@ out:
 out_err:
 	dprintk("NFS: Couldn't create callback socket or server thread; "
 		"err = %d\n", ret);
-	nfs_callback_info.users--;
+	cb_info->users--;
 	goto out;
 }
 
 /*
  * Kill the callback thread if it's no longer being used.
  */
-void nfs_callback_down(void)
+void nfs_callback_down(int minorversion)
 {
+	struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
+
 	mutex_lock(&nfs_callback_mutex);
-	nfs_callback_info.users--;
-	if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL) {
-		kthread_stop(nfs_callback_info.task);
-		svc_exit_thread(nfs_callback_info.rqst);
-		nfs_callback_info.serv = NULL;
-		nfs_callback_info.rqst = NULL;
-		nfs_callback_info.task = NULL;
+	cb_info->users--;
+	if (cb_info->users == 0 && cb_info->task != NULL) {
+		kthread_stop(cb_info->task);
+		svc_exit_thread(cb_info->rqst);
+		cb_info->serv = NULL;
+		cb_info->rqst = NULL;
+		cb_info->task = NULL;
 	}
 	mutex_unlock(&nfs_callback_mutex);
 }
fs/nfs/callback.h +2 -5
index 49ea368..4c69b27 100644
@@ -64,11 +64,8 @@ extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy);
 
 #ifdef CONFIG_NFS_V4
 extern int nfs_callback_up(u32 minorversion, void *args);
-extern void nfs_callback_down(void);
-#else
-#define nfs_callback_up()	(0)
-#define nfs_callback_down()	do {} while(0)
-#endif
+extern void nfs_callback_down(int minorversion);
+#endif /* CONFIG_NFS_V4 */
 
 #ifdef CONFIG_NFS_V4_1
 /*
fs/nfs/client.c +3 -1
index 1c0cae9..dc5b6e7 100644
@@ -193,8 +193,10 @@ static void nfs_free_client(struct nfs_client *clp)
 	if (!IS_ERR(clp->cl_rpcclient))
 		rpc_shutdown_client(clp->cl_rpcclient);
 
+#ifdef CONFIG_NFS_V4
 	if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
-		nfs_callback_down();
+		nfs_callback_down(clp->cl_minorversion);
+#endif /* CONFIG_NFS_V4 */
 
 	if (clp->cl_machine_cred != NULL)
 		put_rpccred(clp->cl_machine_cred);
Signature · 1 line
1.6.0.2
#100 lore raw

RFC 73/85 nfs41: Setup the backchannel

In reply to Benny Halevy

From: Ricardo Labiaga <[email protected]>

The NFS v4.1 callback service has already been setup, and rpc_xprt->serv points to the svc_serv structure describing it. Invoke the xprt_setup_backchannel() initialization to pre- allocate the necessary backchannel structures.

---
 fs/nfs/client.c |    9 ++++++++-
 1 files changed, 8 insertions(+), 1 deletions(-)
fs/nfs/client.c +8 -1
index dc5b6e7..2a24d43 100644
@@ -39,6 +39,7 @@
 #include <linux/nfs_xdr.h>
 #if defined(CONFIG_NFS_V4_1)
 #include <linux/nfs41_session_recovery.h>
+#include <linux/sunrpc/bc_xprt.h>
 #endif /* CONFIG_NFS_V4_1 */
 
 #include <asm/system.h>
@@ -1125,7 +1126,7 @@ error:
 }
 
 /*
- * Allocate and initialize a session if required
+ * Allocate and initialize a session if required, including its backchannel.
  */
 int nfs4_init_session(struct nfs_client *clp, struct rpc_clnt *clnt)
 {
@@ -1145,6 +1146,12 @@ int nfs4_init_session(struct nfs_client *clp, struct rpc_clnt *clnt)
 		else {
 			session->clnt = clnt;
 			session->clp = clp;
+
+			error = xprt_setup_backchannel(clnt->cl_xprt,
+					NFS41_BC_MIN_CALLBACKS);
+			if (error < 0)
+				/* nfs4_put session sets the session to NULL */
+				nfs4_put_session(&session);
 		}
 		clp->cl_session = session;
 	}
Signature · 1 line
1.6.0.2
#101 lore raw

RFC 74/85 nfs41: Client indicates presence of NFSv4.1 callback channel.

In reply to Benny Halevy

From: Ricardo Labiaga <[email protected]>

Set the SESSION4_BACK_CHAN flag to indicate the client supports a backchannel.

---
 fs/nfs/nfs4proc.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
fs/nfs/nfs4proc.c +1 -1
index 28c3cf8..f6a9894 100644
@@ -4679,7 +4679,7 @@ static int _nfs4_proc_create_session(struct nfs4_session *session)
 	int status;
 
 	nfs4_init_channel_attrs(clp, &args.fc_attrs, &args.bc_attrs);
-	args.flags = (SESSION4_PERSIST);
+	args.flags = (SESSION4_PERSIST | SESSION4_BACK_CHAN);
 
 	status = rpc_call_sync(session->clnt, &msg, 0);
 
Signature · 1 line
1.6.0.2
#102 lore raw

RFC 75/85 nfs41: Get the rpc_xprt * from the rpc_rqst instead of the rpc_clnt.

In reply to Benny Halevy

From: Rahul Iyer <[email protected]>

Obtain the rpc_xprt from the rpc_rqst so that calls and callback replies can both use the same code path. A client needs the rpc_xprt in order to reply to a callback.

---
 net/sunrpc/xprt.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)
net/sunrpc/xprt.c +2 -2
index cc1e5f2..e71f1be 100644
@@ -164,8 +164,8 @@ EXPORT_SYMBOL_GPL(xprt_unregister_transport);
  */
 int xprt_reserve_xprt(struct rpc_task *task)
 {
-	struct rpc_xprt	*xprt = task->tk_xprt;
 	struct rpc_rqst *req = task->tk_rqstp;
+	struct rpc_xprt	*xprt = req->rq_xprt;
 
 	if (test_and_set_bit(XPRT_LOCKED, &xprt->state)) {
 		if (task == xprt->snd_task)
@@ -837,7 +837,7 @@ out_unlock:
 
 void xprt_end_transmit(struct rpc_task *task)
 {
-	xprt_release_write(task->tk_xprt, task);
+	xprt_release_write(task->tk_rqstp->rq_xprt, task);
 }
 
 /**
Signature · 1 line
1.6.0.2
#103 lore raw

RFC 76/85 nfs41: Release backchannel resources associated with session

In reply to Benny Halevy

From: Ricardo Labiaga <[email protected]>

Frees the preallocated backchannel resources that are associated with this session when the session is destroyed.

A backchannel is currently created once per session. Destroy the backchannel only when the session is destroyed.

---
 fs/nfs/nfs4proc.c |    6 ++++++
 fs/nfs/super.c    |    1 -
 2 files changed, 6 insertions(+), 1 deletions(-)
fs/nfs/nfs4proc.c +6 -0
index f6a9894..b57ff19 100644
@@ -57,6 +57,7 @@
 #if defined(CONFIG_NFS_V4_1)
 #include "callback.h"
 #include <linux/nfs41_session_recovery.h>
+#include <linux/sunrpc/bc_xprt.h>
 #endif /* CONFIG_NFS_V4_1 */
 
 #define NFSDBG_FACILITY		NFSDBG_PROC
@@ -4588,6 +4589,11 @@ void nfs4_put_session(struct nfs4_session **session)
 {
 	dprintk("--> nfs4_put_session()\n");
 	if (atomic_dec_and_test(&((*session)->ref_count))) {
+		nfs4_proc_destroy_session(*session);
+		dprintk("%s Destroy backchannel for xprt%p\n",
+			__func__, (*session)->clnt->cl_xprt);
+		xprt_destroy_backchannel((*session)->clnt->cl_xprt,
+					 NFS41_BC_MIN_CALLBACKS);
 		nfs4_destroy_slot_table(&((*session)->fore_channel));
 		nfs4_free_session(*session);
 		*session = NULL;
fs/nfs/super.c +0 -1
index d7f99f8..e005889 100644
@@ -2434,7 +2434,6 @@ static void nfs4_kill_super(struct super_block *sb)
 	dprintk("--> %s\n", __func__);
 	nfs_return_all_delegations(sb);
 	kill_anon_super(sb);
-
 	nfs4_renewd_prepare_shutdown(server);
 	nfs_free_server(server);
 	dprintk("<-- %s\n", __func__);
Signature · 1 line
1.6.0.2
#104 lore raw

RFC 77/85 nfs41: store minorversion in cb_compound_hdr_arg

In reply to Benny Halevy
---
 fs/nfs/callback.h     |    1 +
 fs/nfs/callback_xdr.c |   12 +++++++-----
 2 files changed, 8 insertions(+), 5 deletions(-)
fs/nfs/callback.h +1 -0
index 4c69b27..e5ce040 100644
@@ -26,6 +26,7 @@ enum nfs4_callback_opnum {
 struct cb_compound_hdr_arg {
 	unsigned int taglen;
 	const char *tag;
+	unsigned int minorversion;
 	unsigned int callback_ident;
 	unsigned nops;
 };
fs/nfs/callback_xdr.c +7 -5
index dd0ef34..91f6f74 100644
@@ -132,7 +132,6 @@ static __be32 decode_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid)
 static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound_hdr_arg *hdr)
 {
 	__be32 *p;
-	unsigned int minor_version;
 	__be32 status;
 
 	status = decode_string(xdr, &hdr->taglen, &hdr->tag);
@@ -147,15 +146,18 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound
 	p = read_buf(xdr, 12);
 	if (unlikely(p == NULL))
 		return htonl(NFS4ERR_RESOURCE);
-	minor_version = ntohl(*p++);
+	hdr->minorversion = ntohl(*p++);
 	/* Check minor version is zero. */
-	if (minor_version != 0) {
-		printk(KERN_WARNING "%s: NFSv4 server callback with illegal minor version %u!\n",
-				__func__, minor_version);
+	if (hdr->minorversion != 0) {
+		printk(KERN_WARNING "%s: NFSv4 server callback with "
+			"illegal minor version %u!\n",
+			__func__, hdr->minorversion);
 		return htonl(NFS4ERR_MINOR_VERS_MISMATCH);
 	}
 	hdr->callback_ident = ntohl(*p++);
 	hdr->nops = ntohl(*p);
+	dprintk("%s: minorversion %d nops %d\n", __func__,
+		hdr->minorversion, hdr->nops);
 	return 0;
 }
 
Signature · 1 line
1.6.0.2
#105 lore raw

RFC 78/85 nfs41: decode minorversion 1 cb_compound header

In reply to Benny Halevy

decode cb_compound header conforming to
http://tools.ietf.org/html/draft-ietf-nfsv4-minorversion1-26

Get rid of cb_compound_hdr_arg.callback_ident

callback_ident is not used anywhere so we shouldn't waste any memory to store it.

---
 fs/nfs/callback.h     |    1 -
 fs/nfs/callback_xdr.c |   12 ++++++++----
 2 files changed, 8 insertions(+), 5 deletions(-)
fs/nfs/callback.h +0 -1
index e5ce040..f8325da 100644
@@ -27,7 +27,6 @@ struct cb_compound_hdr_arg {
 	unsigned int taglen;
 	const char *tag;
 	unsigned int minorversion;
-	unsigned int callback_ident;
 	unsigned nops;
 };
 
fs/nfs/callback_xdr.c +8 -4
index 91f6f74..e141a7e 100644
@@ -143,18 +143,22 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound
 				__func__, hdr->taglen);
 		return htonl(NFS4ERR_RESOURCE);
 	}
-	p = read_buf(xdr, 12);
+	p = read_buf(xdr, 4);
 	if (unlikely(p == NULL))
 		return htonl(NFS4ERR_RESOURCE);
 	hdr->minorversion = ntohl(*p++);
-	/* Check minor version is zero. */
-	if (hdr->minorversion != 0) {
+	/* Check minor version is zero or one. */
+	if (hdr->minorversion <= 1) {
+		p = read_buf(xdr, 8);
+		if (unlikely(p == NULL))
+			return htonl(NFS4ERR_RESOURCE);
+		p++;	/* skip callback_ident */
+	} else {
 		printk(KERN_WARNING "%s: NFSv4 server callback with "
 			"illegal minor version %u!\n",
 			__func__, hdr->minorversion);
 		return htonl(NFS4ERR_MINOR_VERS_MISMATCH);
 	}
-	hdr->callback_ident = ntohl(*p++);
 	hdr->nops = ntohl(*p);
 	dprintk("%s: minorversion %d nops %d\n", __func__,
 		hdr->minorversion, hdr->nops);
Signature · 1 line
1.6.0.2
#106 lore raw

RFC 79/85 nfs41: callback numbers definitions

In reply to Benny Halevy
---
 fs/nfs/callback.h |   10 ++++++++++
 1 files changed, 10 insertions(+), 0 deletions(-)
fs/nfs/callback.h +10 -0
index f8325da..9cb0451 100644
@@ -20,6 +20,16 @@ enum nfs4_callback_procnum {
 enum nfs4_callback_opnum {
 	OP_CB_GETATTR = 3,
 	OP_CB_RECALL  = 4,
+/* Callback operations new to NFSv4.1 */
+	OP_CB_LAYOUTRECALL  = 5,
+	OP_CB_NOTIFY        = 6,
+	OP_CB_PUSH_DELEG    = 7,
+	OP_CB_RECALL_ANY    = 8,
+	OP_CB_RECALLABLE_OBJ_AVAIL = 9,
+	OP_CB_RECALL_SLOT   = 10,
+	OP_CB_SEQUENCE      = 11,
+	OP_CB_WANTS_CANCELLED = 12,
+	OP_CB_NOTIFY_LOCK   = 13,
 	OP_CB_ILLEGAL = 10044,
 };
 
Signature · 1 line
1.6.0.2
#107 lore raw

RFC 80/85 nfs41: consider minorversion in callback_xdr:process_op

In reply to Benny Halevy
---
 fs/nfs/callback_xdr.c |   63 ++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 50 insertions(+), 13 deletions(-)
fs/nfs/callback_xdr.c +50 -13
index e141a7e..dd25794 100644
@@ -359,31 +359,61 @@ out:
 	return status;
 }
 
-static __be32 process_op(struct svc_rqst *rqstp,
+static __be32 process_op(uint32_t minorversion, int nop,
+		struct svc_rqst *rqstp,
 		struct xdr_stream *xdr_in, void *argp,
 		struct xdr_stream *xdr_out, void *resp)
 {
 	struct callback_op *op = &callback_ops[0];
 	unsigned int op_nr = OP_CB_ILLEGAL;
-	__be32 status = 0;
+	__be32 status;
 	long maxlen;
 	__be32 res;
 
 	dprintk("%s: start\n", __func__);
 	status = decode_op_hdr(xdr_in, &op_nr);
-	if (likely(status == 0)) {
+	if (unlikely(status))
+		goto out_illegal;
+
+	dprintk("%s: minorversion=%d nop=%d op_nr=%u\n",
+		__func__, minorversion, nop, op_nr);
+#if defined(CONFIG_NFS_V4_1)
+	if (minorversion == 1) {
 		switch (op_nr) {
-			case OP_CB_GETATTR:
-			case OP_CB_RECALL:
-				op = &callback_ops[op_nr];
-				break;
-			default:
-				op_nr = OP_CB_ILLEGAL;
-				op = &callback_ops[0];
-				status = htonl(NFS4ERR_OP_ILLEGAL);
+		case OP_CB_GETATTR:
+		case OP_CB_RECALL:
+			op = &callback_ops[op_nr];
+			break;
+
+		case OP_CB_LAYOUTRECALL:
+		case OP_CB_NOTIFY:
+		case OP_CB_PUSH_DELEG:
+		case OP_CB_RECALL_ANY:
+		case OP_CB_RECALLABLE_OBJ_AVAIL:
+		case OP_CB_RECALL_SLOT:
+		case OP_CB_SEQUENCE:
+		case OP_CB_WANTS_CANCELLED:
+		case OP_CB_NOTIFY_LOCK:
+			op = &callback_ops[0];
+			status = htonl(NFS4ERR_NOTSUPP);
+			break;
+		default:
+			goto out_illegal;
 		}
-	}
 
+		goto out;
+	}
+#endif /* defined(CONFIG_NFS_V4_1) */
+
+	switch (op_nr) {
+	case OP_CB_GETATTR:
+	case OP_CB_RECALL:
+		op = &callback_ops[op_nr];
+		break;
+	default:
+		goto out_illegal;
+	}
+out:
 	maxlen = xdr_out->end - xdr_out->p;
 	if (maxlen > 0 && maxlen < PAGE_SIZE) {
 		if (likely(status == 0 && op->decode_args != NULL))
@@ -400,6 +430,12 @@ static __be32 process_op(struct svc_rqst *rqstp,
 		status = op->encode_res(rqstp, xdr_out, resp);
 	dprintk("%s: done, status = %d\n", __func__, ntohl(status));
 	return status;
+
+out_illegal:
+	op_nr = OP_CB_ILLEGAL;
+	op = &callback_ops[0];
+	status = htonl(NFS4ERR_OP_ILLEGAL);
+	goto out;
 }
 
 /*
@@ -431,7 +467,8 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
 		return rpc_system_err;
 
 	while (status == 0 && nops != hdr_arg.nops) {
-		status = process_op(rqstp, &xdr_in, argp, &xdr_out, resp);
+		status = process_op(hdr_arg.minorversion, nops,
+				    rqstp, &xdr_in, argp, &xdr_out, resp);
 		nops++;
 	}
 
Signature · 1 line
1.6.0.2
#108 lore raw

RFC 81/85 nfs41: define CB_NOTIFY_DEVICEID as not supported

In reply to Benny Halevy

From: Ricardo Labiaga <[email protected]>

Need to return NFS4ERR_NOTSUPP for CB_NOTIFY_DEVICEID rather than NFS4ERR_OP_ILLEGAL.

---
 fs/nfs/callback.h     |    1 +
 fs/nfs/callback_xdr.c |    1 +
 2 files changed, 2 insertions(+), 0 deletions(-)
fs/nfs/callback.h +1 -0
index 9cb0451..b3ccfe8 100644
@@ -30,6 +30,7 @@ enum nfs4_callback_opnum {
 	OP_CB_SEQUENCE      = 11,
 	OP_CB_WANTS_CANCELLED = 12,
 	OP_CB_NOTIFY_LOCK   = 13,
+	OP_CB_NOTIFY_DEVICEID = 14,
 	OP_CB_ILLEGAL = 10044,
 };
 
fs/nfs/callback_xdr.c +1 -0
index dd25794..9db88ce 100644
@@ -394,6 +394,7 @@ static __be32 process_op(uint32_t minorversion, int nop,
 		case OP_CB_SEQUENCE:
 		case OP_CB_WANTS_CANCELLED:
 		case OP_CB_NOTIFY_LOCK:
+		case OP_CB_NOTIFY_DEVICEID:
 			op = &callback_ops[0];
 			status = htonl(NFS4ERR_NOTSUPP);
 			break;
Signature · 1 line
1.6.0.2
#109 lore raw

RFC 82/85 nfs41: cb_sequence protocol level data structures

In reply to Benny Halevy
---
 fs/nfs/callback.h |   35 +++++++++++++++++++++++++++++++++++
 1 files changed, 35 insertions(+), 0 deletions(-)
fs/nfs/callback.h +35 -0
index b3ccfe8..5616257 100644
@@ -70,6 +70,41 @@ struct cb_recallargs {
 	uint32_t truncate;
 };
 
+#if defined(CONFIG_NFS_V4_1)
+
+struct referring_call {
+	uint32_t			rc_sequenceid;
+	uint32_t			rc_slotid;
+};
+
+struct referring_call_list {
+	nfs41_sessionid			rcl_sessionid;
+	uint32_t			rcl_nrefcalls;
+	struct referring_call 		*rcl_refcalls;
+};
+
+struct cb_sequenceargs {
+	struct sockaddr_in		*csa_addr;
+	nfs41_sessionid			csa_sessionid;
+	uint32_t			csa_sequenceid;
+	uint32_t			csa_slotid;
+	uint32_t			csa_highestslotid;
+	uint32_t			csa_cachethis;
+	uint32_t			csa_nrclists;
+	struct referring_call_list	*csa_rclists;
+};
+
+struct cb_sequenceres {
+	uint32_t			csr_status;
+	nfs41_sessionid			csr_sessionid;
+	uint32_t			csr_sequenceid;
+	uint32_t			csr_slotid;
+	uint32_t			csr_highestslotid;
+	uint32_t			csr_target_highestslotid;
+};
+
+#endif /* CONFIG_NFS_V4_1 */
+
 extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res);
 extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy);
 
Signature · 1 line
1.6.0.2
#110 lore raw

RFC 83/85 nfs41: cb_sequence proc implementation

In reply to Benny Halevy

Currently, just free up any referring calls information.

---
 fs/nfs/callback.h      |    4 ++++
 fs/nfs/callback_proc.c |   30 ++++++++++++++++++++++++++++++
 2 files changed, 34 insertions(+), 0 deletions(-)
fs/nfs/callback.h +4 -0
index 5616257..81a9df6 100644
@@ -119,6 +119,10 @@ extern void nfs_callback_down(int minorversion);
  * so we limit their concurrency to 1.
  */
 #define NFS41_BC_MIN_CALLBACKS 1
+#define NFS41_BC_MAX_CALLBACKS 1
+
+extern unsigned nfs4_callback_sequence(struct cb_sequenceargs *args,
+				       struct cb_sequenceres *res);
 #endif /* CONFIG_NFS_V4_1 */
 
 extern unsigned int nfs_callback_set_tcpport;
fs/nfs/callback_proc.c +30 -0
index f7e83e2..63b8662 100644
@@ -101,3 +101,33 @@ out:
 	dprintk("%s: exit with status = %d\n", __func__, ntohl(res));
 	return res;
 }
+
+#if defined(CONFIG_NFS_V4_1)
+
+/* FIXME: validate args->cbs_{sequence,slot}id */
+/* FIXME: referring calls should be processed */
+unsigned nfs4_callback_sequence(struct cb_sequenceargs *args,
+				struct cb_sequenceres *res)
+{
+	int i;
+	unsigned status = 0;
+
+	for (i = 0; i < args->csa_nrclists; i++)
+		kfree(args->csa_rclists[i].rcl_refcalls);
+	kfree(args->csa_rclists);
+
+	memcpy(&res->csr_sessionid, &args->csa_sessionid,
+	       sizeof(res->csr_sessionid));
+	res->csr_sequenceid = args->csa_sequenceid;
+	res->csr_slotid = args->csa_slotid;
+	res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS;
+	if (res->csr_highestslotid > args->csa_highestslotid)
+		res->csr_highestslotid = args->csa_highestslotid;
+	res->csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS;
+
+	dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
+	res->csr_status = status;
+	return status;
+}
+
+#endif /* CONFIG_NFS_V4_1 */
Signature · 1 line
1.6.0.2
#111 lore raw

RFC 84/85 nfs41: cb_sequence xdr implementation

In reply to Benny Halevy
---
 fs/nfs/callback_xdr.c |  191 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 189 insertions(+), 2 deletions(-)
fs/nfs/callback_xdr.c +189 -2
index 9db88ce..7b96b09 100644
@@ -20,8 +20,28 @@
 				2 + 2 + 3 + 3)
 #define CB_OP_RECALL_RES_MAXSZ	(CB_OP_HDR_RES_MAXSZ)
 
+#if defined(CONFIG_NFS_V4_1)
+#define CB_OP_SEQUENCE_RES_MAXSZ	(CB_OP_HDR_RES_MAXSZ + \
+					4 + 1 + 3)
+#endif /* CONFIG_NFS_V4_1 */
+
 #define NFSDBG_FACILITY NFSDBG_CALLBACK
 
+#define READ64(x)         do {			\
+	(x) = (u64)ntohl(*p++) << 32;		\
+	(x) |= ntohl(*p++);			\
+} while (0)
+
+#define READMEM(x,nbytes) do {			\
+	x = (char *)p;				\
+	p += XDR_QUADLEN(nbytes);		\
+} while (0)
+
+#define COPYMEM(x,nbytes) do {			\
+	memcpy((x), p, nbytes);			\
+	p += XDR_QUADLEN(nbytes);		\
+} while (0)
+
 typedef __be32 (*callback_process_op_t)(void *, void *);
 typedef __be32 (*callback_decode_arg_t)(struct svc_rqst *, struct xdr_stream *, void *);
 typedef __be32 (*callback_encode_res_t)(struct svc_rqst *, struct xdr_stream *, void *);
@@ -210,6 +230,122 @@ out:
 	return status;
 }
 
+#if defined(CONFIG_NFS_V4_1)
+
+static unsigned decode_sessionid(struct xdr_stream *xdr,
+				 nfs41_sessionid *sid)
+{
+	uint32_t *p;
+	int len = 16;
+
+	p = read_buf(xdr, len);
+	if (unlikely(p == NULL))
+		return htonl(NFS4ERR_RESOURCE);;
+
+	memcpy(sid, p, len);
+	return 0;
+}
+
+static unsigned decode_rc_list(struct xdr_stream *xdr,
+			       struct referring_call_list *rc_list)
+{
+	uint32_t *p;
+	int i;
+	unsigned status;
+
+	status = decode_sessionid(xdr, &rc_list->rcl_sessionid);
+	if (status)
+		goto out;
+
+	status = htonl(NFS4ERR_RESOURCE);
+	p = read_buf(xdr, sizeof(uint32_t));
+	if (unlikely(p == NULL))
+		goto out;
+
+	rc_list->rcl_nrefcalls = ntohl(*p++);
+	if (rc_list->rcl_nrefcalls) {
+		p = read_buf(xdr,
+			     rc_list->rcl_nrefcalls * 2 * sizeof(uint32_t));
+		if (unlikely(p == NULL))
+			goto out;
+		rc_list->rcl_refcalls = kmalloc(rc_list->rcl_nrefcalls *
+						sizeof(*rc_list->rcl_refcalls),
+						GFP_KERNEL);
+		if (unlikely(rc_list->rcl_refcalls == NULL))
+			goto out;
+		for (i = 0; i < rc_list->rcl_nrefcalls; i++) {
+			rc_list->rcl_refcalls[i].rc_sequenceid = ntohl(*p++);
+			rc_list->rcl_refcalls[i].rc_slotid = ntohl(*p++);
+		}
+	}
+	status = 0;
+
+out:
+	return status;
+}
+
+static unsigned decode_cb_sequence_args(struct svc_rqst *rqstp,
+					struct xdr_stream *xdr,
+					struct cb_sequenceargs *args)
+{
+	uint32_t *p;
+	int i;
+	unsigned status;
+
+	status = decode_sessionid(xdr, &args->csa_sessionid);
+	if (status)
+		goto out;
+
+	status = htonl(NFS4ERR_RESOURCE);
+	p = read_buf(xdr, 5 * sizeof(uint32_t));
+	if (unlikely(p == NULL))
+		goto out;
+
+	args->csa_addr = svc_addr_in(rqstp);
+	args->csa_sequenceid = ntohl(*p++);
+	args->csa_slotid = ntohl(*p++);
+	args->csa_highestslotid = ntohl(*p++);
+	args->csa_cachethis = ntohl(*p++);
+	args->csa_nrclists = ntohl(*p++);
+	args->csa_rclists = NULL;
+	if (args->csa_nrclists) {
+		args->csa_rclists = kmalloc(args->csa_nrclists *
+					    sizeof(*args->csa_rclists),
+					    GFP_KERNEL);
+		if (unlikely(args->csa_rclists == NULL))
+			goto out;
+
+		for (i = 0; i < args->csa_nrclists; i++) {
+			status = decode_rc_list(xdr, &args->csa_rclists[i]);
+			if (status)
+				goto out_free;
+		}
+	}
+	status = 0;
+
+	dprintk("%s: sessionid %x:%x:%x:%x sequenceid %u slotid %u "
+		"highestslotid %u cachethis %d nrclists %u\n",
+		__func__,
+		((u32 *)&args->csa_sessionid)[0],
+		((u32 *)&args->csa_sessionid)[1],
+		((u32 *)&args->csa_sessionid)[2],
+		((u32 *)&args->csa_sessionid)[3],
+		args->csa_sequenceid, args->csa_slotid,
+		args->csa_highestslotid, args->csa_cachethis,
+		args->csa_nrclists);
+out:
+	dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
+	return status;
+
+out_free:
+	for (i = 0; i < args->csa_nrclists; i++)
+		kfree(args->csa_rclists[i].rcl_refcalls);
+	kfree(args->csa_rclists);
+	goto out;
+}
+
+#endif /* CONFIG_NFS_V4_1 */
+
 static __be32 encode_string(struct xdr_stream *xdr, unsigned int len, const char *str)
 {
 	__be32 *p;
@@ -359,6 +495,49 @@ out:
 	return status;
 }
 
+#if defined(CONFIG_NFS_V4_1)
+
+static unsigned encode_sessionid(struct xdr_stream *xdr,
+				 const nfs41_sessionid *sid)
+{
+	uint32_t *p;
+	int len = 4 * sizeof(uint32_t);
+
+	p = xdr_reserve_space(xdr, len);
+	if (unlikely(p == NULL))
+		return htonl(NFS4ERR_RESOURCE);
+
+	memcpy(p, sid, len);
+	return 0;
+}
+
+static unsigned encode_cb_sequence_res(struct svc_rqst *rqstp,
+				       struct xdr_stream *xdr,
+				       const struct cb_sequenceres *res)
+{
+	uint32_t *p;
+	unsigned status = res->csr_status;
+
+	if (unlikely(status != 0))
+		goto out;
+
+	encode_sessionid(xdr, &res->csr_sessionid);
+
+	p = xdr_reserve_space(xdr, 4 * sizeof(uint32_t));
+	if (unlikely(p == NULL))
+		return htonl(NFS4ERR_RESOURCE);
+
+	*p++ = htonl(res->csr_sequenceid);
+	*p++ = htonl(res->csr_slotid);
+	*p++ = htonl(res->csr_highestslotid);
+	*p++ = htonl(res->csr_target_highestslotid);
+out:
+	dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
+	return status;
+}
+
+#endif /* CONFIG_NFS_V4_1 */
+
 static __be32 process_op(uint32_t minorversion, int nop,
 		struct svc_rqst *rqstp,
 		struct xdr_stream *xdr_in, void *argp,
@@ -382,6 +561,7 @@ static __be32 process_op(uint32_t minorversion, int nop,
 		switch (op_nr) {
 		case OP_CB_GETATTR:
 		case OP_CB_RECALL:
+		case OP_CB_SEQUENCE:
 			op = &callback_ops[op_nr];
 			break;
 
@@ -391,7 +571,6 @@ static __be32 process_op(uint32_t minorversion, int nop,
 		case OP_CB_RECALL_ANY:
 		case OP_CB_RECALLABLE_OBJ_AVAIL:
 		case OP_CB_RECALL_SLOT:
-		case OP_CB_SEQUENCE:
 		case OP_CB_WANTS_CANCELLED:
 		case OP_CB_NOTIFY_LOCK:
 		case OP_CB_NOTIFY_DEVICEID:
@@ -496,7 +675,15 @@ static struct callback_op callback_ops[] = {
 		.process_op = (callback_process_op_t)nfs4_callback_recall,
 		.decode_args = (callback_decode_arg_t)decode_recall_args,
 		.res_maxsize = CB_OP_RECALL_RES_MAXSZ,
-	}
+	},
+#if defined(CONFIG_NFS_V4_1)
+	[OP_CB_SEQUENCE] = {
+		.process_op = (callback_process_op_t)nfs4_callback_sequence,
+		.decode_args = (callback_decode_arg_t)decode_cb_sequence_args,
+		.encode_res = (callback_encode_res_t)encode_cb_sequence_res,
+		.res_maxsize = CB_OP_SEQUENCE_RES_MAXSZ,
+	},
+#endif /* CONFIG_NFS_V4_1 */
 };
 
 /*
Signature · 1 line
1.6.0.2
#112 lore raw

RFC 85/85 nfs41: verify CB_SEQUENCE position in callback compound

In reply to Benny Halevy

CB_SEQUENCE must appear first in the callback compound RPC. If it is not the first operation NFS4ERR_SEQUENCE_POS must be returned. If the first operation ni the CB_COMPOUND is not CB_SEQUENCE then NFS4ERR_OP_NOT_IN_SESSION must be returned.

---
 fs/nfs/callback_xdr.c |    8 ++++++++
 1 files changed, 8 insertions(+), 0 deletions(-)
fs/nfs/callback_xdr.c +8 -0
index 7b96b09..e0bd42b 100644
@@ -558,6 +558,14 @@ static __be32 process_op(uint32_t minorversion, int nop,
 		__func__, minorversion, nop, op_nr);
 #if defined(CONFIG_NFS_V4_1)
 	if (minorversion == 1) {
+		if (op_nr == OP_CB_SEQUENCE) {
+			if (nop != 0) {
+				status = htonl(NFS4ERR_SEQUENCE_POS);
+				goto out;
+			}
+		} else if (nop == 0)
+			status = htonl(NFS4ERR_OP_NOT_IN_SESSION);
+
 		switch (op_nr) {
 		case OP_CB_GETATTR:
 		case OP_CB_RECALL:
Signature · 1 line
1.6.0.2
#113 lore raw
In reply to Benny Halevy

The patchset is also available on
git://linux-nfs.org/~bhalevy/linux-pnfs.git nfs41-2.6.28-review

Cumulated diff for the client side changes:

http://git.linux-nfs.org/?p=bhalevy/linux-pnfs.git;a=commitdiff_plain;h=nfs41-2.6.28-rc4-client-review-2008-11-10;hp=upstream-2.6.28

Benny

Quoted 125 lines · Benny Halevy

Trond,

Here's the patch series for nfs41 client side, rebased over 2.6.28 for review.

I've cleaned up the patch as much as I could in the given time frame but surely there's more work to be done.

I encourage others to comment on the patches as well.

Thanks,

Benny

pre-nfs41 patches:
[RFC 01/85] sunrpc: get rid of rpc_rqst.rq_bufsize
[RFC 02/85] nfs: return compound hdr.status when there are no op replies [RFC 03/85] nfs: remove incorrect usage of nfs4 compound response hdr.status [RFC 04/85] FIXME: NFS: Increment operation number in each encode_* routine

general defs:
[RFC 05/85] nfs41: Add Kconfig symbols for NFSv4.1
[RFC 06/85] nfs41: common protocol definitions
[RFC 07/85] nfs41: define NFS4_MAX_MINOR_VERSION based on CONFIG_NFS_V4_1 [RFC 08/85] nfs41: client xdr definitions

basic support for minorversion != 0:
[RFC 09/85] nfs41: add mount command option minorvers
[RFC 10/85] nfs41: struct nfs_server minorversion
[RFC 11/85] nfs41: nfs_client.cl_minorversion
[RFC 12/85] nfs41: set nfs_client rpc_ops based on minorversion
[RFC 13/85] nfs41: use ptr to rpc procedures via struct nfs_client
[RFC 14/85] nfs41: sunrpc: support minorversion for rpc_clnt version table. [RFC 15/85] nfs41: Proper initialization of nfs_client_initdata.rpc_ops
[RFC 16/85] nfs41: pass recovery status from reclaimer kthread up to nfs4_wait_clnt_recover [RFC 17/85] nfs41: fallback to lower minorversion if nfs4_create_server fails

sessions basic functionality:
[RFC 18/85] nfs41: sessions client infrastructure [RFC 19/85] nfs41: share client sessions across mounts [RFC 20/85] nfs41: slot table init and destroy [RFC 21/85] nfs41: find slot
[RFC 22/85] nfs41: free slot

support for sequence for legacy ops:
[RFC 23/85] nfs41: minorversion support for xdr [RFC 24/85] nfs41: stubs for nfs41 procedures [RFC 25/85] nfs41: make nfs4_wait_bit_killable public [RFC 26/85] nfs41: introduce nfs4_call_sync [RFC 27/85] nfs41: nfs4_setup_sequence
[RFC 28/85] nfs41: setup_sequence method
[RFC 29/85] nfs41: nfs41_sequence_done
[RFC 30/85] nfs41: nfs41_call_sync_done
[RFC 31/85] nfs41: separate free slot from sequence done [RFC 32/85] nfs41: sequence setup/done support [RFC 33/85] nfs41: Support sessions with O_DIRECT.

nfs41 ops:
[RFC 34/85] nfs41: exchange_id operation
[RFC 35/85] nfs41: get_lease_time
[RFC 36/85] nfs41: create_session operation
[RFC 37/85] nfs41: destroy_session operation
[RFC 38/85] nfs41: sequence operation

session setup and recovery:
[RFC 39/85] nfs41: session recovery infrastructure
[RFC 40/85] nfs41: schedule async session reset
[RFC 41/85] nfs41: lease renewal
[RFC 42/85] nfs41: sunrpc: Export the call prepare state for session reset [RFC 43/85] nfs41: use rpc prepare call state for session reset [RFC 44/85] nfs41: kick start nfs41 session recovery when handling errors [RFC 45/85] nfs41: start session recovery from nfs4_setup_sequence [RFC 46/85] nfs41: introduce get_state_renewal_cred
[RFC 47/85] nfs41: get cred in exchange_id when cred arg is NULL [RFC 48/85] nfs41: establish sessions-based clientid
[RFC 49/85] nfs41: recover lease in _nfs4_lookup_root
[RFC 50/85] nfs41: schedule state recovery on BAD or DEAD session [RFC 51/85] nfs41: state reclaimer renew lease error handling [RFC 52/85] nfs41: increment_{open,lock}_seqid

backchannel:
[RFC 53/85] nfs41: Add ability to read RPC call direction on TCP stream. [RFC 54/85] nfs41: Skip past the RPC call direction
[RFC 55/85] nfs41: Refactor NFSv4 callback service
[RFC 56/85] nfs41: client callback structures
[RFC 57/85] nfs41: Initialize new rpc_xprt callback related fields [RFC 58/85] nfs41: New backchannel helper routines
[RFC 59/85] nfs41: New include/linux/sunrpc/bc_xprt.h
[RFC 60/85] nfs41: New xs_tcp_read_data()
[RFC 61/85] nfs41: Add backchannel processing support to RPC state machine [RFC 62/85] nfs41: Backchannel callback service helper routines [RFC 63/85] FIXME: nfs41: sunrpc: handle clnt==NULL in call_status

callback service:
[RFC 64/85] nfs41: Refactor svc_process()
[RFC 65/85] nfs41: Backchannel bc_svc_process()
[RFC 66/85] nfs41: Implement NFSv4.1 callback service process.
[RFC 67/85] nfs41: sunrpc: provide functions to create and destroy a svc_xprt for backchannel use [RFC 68/85] nfs41: sunrpc: add a struct svc_xprt pointer to struct svc_serv for backchannel use [RFC 69/85] nfs41: create a svc_xprt for nfs41 callback thread and use for incoming callbacks [RFC 70/85] nfs41: save svc_serv in nfs_callback_info
[RFC 71/85] nfs41: Add a reference to svc_serv during callback service bring up [RFC 72/85] nfs41: Allow NFSv4 and NFSv4.1 callback services to coexist
[RFC 73/85] nfs41: Setup the backchannel
[RFC 74/85] nfs41: Client indicates presence of NFSv4.1 callback channel.
[RFC 75/85] nfs41: Get the rpc_xprt * from the rpc_rqst instead of the rpc_clnt. [RFC 76/85] nfs41: Release backchannel resources associated with session

callback processing:
[RFC 77/85] nfs41: store minorversion in cb_compound_hdr_arg [RFC 78/85] nfs41: decode minorversion 1 cb_compound header [RFC 79/85] nfs41: callback numbers definitions
[RFC 80/85] nfs41: consider minorversion in callback_xdr:process_op [RFC 81/85] nfs41: define CB_NOTIFY_DEVICEID as not supported [RFC 82/85] nfs41: cb_sequence protocol level data structures [RFC 83/85] nfs41: cb_sequence proc implementation
[RFC 84/85] nfs41: cb_sequence xdr implementation
[RFC 85/85] nfs41: verify CB_SEQUENCE position in callback compound
_______________________________________________
pNFS mailing list
[email protected]
http://linux-nfs.org/cgi-bin/mailman/listinfo/pnfs

#114 lore raw
In reply to Benny Halevy
Quoted 9 lines · Benny Halevy

Trond,

Here's the patch series for nfs41 client side, rebased over 2.6.28 for review.

I've cleaned up the patch as much as I could in the given time frame but surely there's more work to be done.

I encourage others to comment on the patches as well.

Yipes! Well, no shortage of reading material now....

--b.

Quoted 116 lines

Thanks,

Benny

pre-nfs41 patches:
[RFC 01/85] sunrpc: get rid of rpc_rqst.rq_bufsize
[RFC 02/85] nfs: return compound hdr.status when there are no op replies [RFC 03/85] nfs: remove incorrect usage of nfs4 compound response hdr.status [RFC 04/85] FIXME: NFS: Increment operation number in each encode_* routine

general defs:
[RFC 05/85] nfs41: Add Kconfig symbols for NFSv4.1
[RFC 06/85] nfs41: common protocol definitions
[RFC 07/85] nfs41: define NFS4_MAX_MINOR_VERSION based on CONFIG_NFS_V4_1 [RFC 08/85] nfs41: client xdr definitions

basic support for minorversion != 0:
[RFC 09/85] nfs41: add mount command option minorvers
[RFC 10/85] nfs41: struct nfs_server minorversion
[RFC 11/85] nfs41: nfs_client.cl_minorversion
[RFC 12/85] nfs41: set nfs_client rpc_ops based on minorversion
[RFC 13/85] nfs41: use ptr to rpc procedures via struct nfs_client
[RFC 14/85] nfs41: sunrpc: support minorversion for rpc_clnt version table. [RFC 15/85] nfs41: Proper initialization of nfs_client_initdata.rpc_ops
[RFC 16/85] nfs41: pass recovery status from reclaimer kthread up to nfs4_wait_clnt_recover [RFC 17/85] nfs41: fallback to lower minorversion if nfs4_create_server fails

sessions basic functionality:
[RFC 18/85] nfs41: sessions client infrastructure [RFC 19/85] nfs41: share client sessions across mounts [RFC 20/85] nfs41: slot table init and destroy [RFC 21/85] nfs41: find slot
[RFC 22/85] nfs41: free slot

support for sequence for legacy ops:
[RFC 23/85] nfs41: minorversion support for xdr [RFC 24/85] nfs41: stubs for nfs41 procedures [RFC 25/85] nfs41: make nfs4_wait_bit_killable public [RFC 26/85] nfs41: introduce nfs4_call_sync [RFC 27/85] nfs41: nfs4_setup_sequence
[RFC 28/85] nfs41: setup_sequence method
[RFC 29/85] nfs41: nfs41_sequence_done
[RFC 30/85] nfs41: nfs41_call_sync_done
[RFC 31/85] nfs41: separate free slot from sequence done [RFC 32/85] nfs41: sequence setup/done support [RFC 33/85] nfs41: Support sessions with O_DIRECT.

nfs41 ops:
[RFC 34/85] nfs41: exchange_id operation
[RFC 35/85] nfs41: get_lease_time
[RFC 36/85] nfs41: create_session operation
[RFC 37/85] nfs41: destroy_session operation
[RFC 38/85] nfs41: sequence operation

session setup and recovery:
[RFC 39/85] nfs41: session recovery infrastructure
[RFC 40/85] nfs41: schedule async session reset
[RFC 41/85] nfs41: lease renewal
[RFC 42/85] nfs41: sunrpc: Export the call prepare state for session reset [RFC 43/85] nfs41: use rpc prepare call state for session reset [RFC 44/85] nfs41: kick start nfs41 session recovery when handling errors [RFC 45/85] nfs41: start session recovery from nfs4_setup_sequence [RFC 46/85] nfs41: introduce get_state_renewal_cred
[RFC 47/85] nfs41: get cred in exchange_id when cred arg is NULL [RFC 48/85] nfs41: establish sessions-based clientid
[RFC 49/85] nfs41: recover lease in _nfs4_lookup_root
[RFC 50/85] nfs41: schedule state recovery on BAD or DEAD session [RFC 51/85] nfs41: state reclaimer renew lease error handling [RFC 52/85] nfs41: increment_{open,lock}_seqid

backchannel:
[RFC 53/85] nfs41: Add ability to read RPC call direction on TCP stream. [RFC 54/85] nfs41: Skip past the RPC call direction
[RFC 55/85] nfs41: Refactor NFSv4 callback service
[RFC 56/85] nfs41: client callback structures
[RFC 57/85] nfs41: Initialize new rpc_xprt callback related fields [RFC 58/85] nfs41: New backchannel helper routines
[RFC 59/85] nfs41: New include/linux/sunrpc/bc_xprt.h
[RFC 60/85] nfs41: New xs_tcp_read_data()
[RFC 61/85] nfs41: Add backchannel processing support to RPC state machine [RFC 62/85] nfs41: Backchannel callback service helper routines [RFC 63/85] FIXME: nfs41: sunrpc: handle clnt==NULL in call_status

callback service:
[RFC 64/85] nfs41: Refactor svc_process()
[RFC 65/85] nfs41: Backchannel bc_svc_process()
[RFC 66/85] nfs41: Implement NFSv4.1 callback service process.
[RFC 67/85] nfs41: sunrpc: provide functions to create and destroy a svc_xprt for backchannel use [RFC 68/85] nfs41: sunrpc: add a struct svc_xprt pointer to struct svc_serv for backchannel use [RFC 69/85] nfs41: create a svc_xprt for nfs41 callback thread and use for incoming callbacks [RFC 70/85] nfs41: save svc_serv in nfs_callback_info
[RFC 71/85] nfs41: Add a reference to svc_serv during callback service bring up [RFC 72/85] nfs41: Allow NFSv4 and NFSv4.1 callback services to coexist
[RFC 73/85] nfs41: Setup the backchannel
[RFC 74/85] nfs41: Client indicates presence of NFSv4.1 callback channel.
[RFC 75/85] nfs41: Get the rpc_xprt * from the rpc_rqst instead of the rpc_clnt. [RFC 76/85] nfs41: Release backchannel resources associated with session

callback processing:
[RFC 77/85] nfs41: store minorversion in cb_compound_hdr_arg [RFC 78/85] nfs41: decode minorversion 1 cb_compound header [RFC 79/85] nfs41: callback numbers definitions
[RFC 80/85] nfs41: consider minorversion in callback_xdr:process_op [RFC 81/85] nfs41: define CB_NOTIFY_DEVICEID as not supported [RFC 82/85] nfs41: cb_sequence protocol level data structures [RFC 83/85] nfs41: cb_sequence proc implementation
[RFC 84/85] nfs41: cb_sequence xdr implementation
[RFC 85/85] nfs41: verify CB_SEQUENCE position in callback compound
_______________________________________________
pNFS mailing list
[email protected]
http://linux-nfs.org/cgi-bin/mailman/listinfo/pnfs