From 1efae38140546db403845d628db9f2d608caa87e Mon Sep 17 00:00:00 2001 From: Ricardo Labiaga Date: Wed, 1 Apr 2009 09:21:46 -0400 Subject: nfs41: Add Kconfig symbols for NFSv4.1 Added CONFIG_NFS_V4_1 and made it depend upon CONFIG_NFS_V4 and EXPERIMENTAL. Indicate that CONFIG_NFS_V4_1 is for NFS developers at the moment At the moment we're expecting folks trying out nfs41 to actively participate in the development process by helping us debug issues and ideally send patches to fix problems. Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/Kconfig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index e67f3ec0773..5d6d6f41593 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -74,6 +74,15 @@ config NFS_V4 If unsure, say N. +config NFS_V4_1 + bool "NFS client support for NFSv4.1 (DEVELOPER ONLY)" + depends on NFS_V4 && EXPERIMENTAL + help + This option enables support for minor version 1 of the NFSv4 protocol + (draft-ietf-nfsv4-minorversion1) in the kernel's NFS client. + + Unless you're an NFS developer, say N. + config ROOT_NFS bool "Root file system on NFS" depends on NFS_FS=y && IP_PNP -- cgit v1.2.3 From 44549dff82753b6a5ffabcefeead34be63e95d96 Mon Sep 17 00:00:00 2001 From: Mike Sager Date: Wed, 1 Apr 2009 09:21:47 -0400 Subject: nfs41: define NFS4_MAX_MINOR_VERSION based on CONFIG_NFS_V4_1 If 4.1 isn't supported, NFS4_MAX_MINOR_VERSION will be 0. Signed-off-by: Mike Sager Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- include/linux/nfs4.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index e3f0cbcbd0d..7c36fcf2dfb 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -462,6 +462,13 @@ enum lock_type4 { #define NFSPROC4_NULL 0 #define NFSPROC4_COMPOUND 1 #define NFS4_MINOR_VERSION 0 + +#if defined(CONFIG_NFS_V4_1) +#define NFS4_MAX_MINOR_VERSION 1 +#else +#define NFS4_MAX_MINOR_VERSION 0 +#endif /* CONFIG_NFS_V4_1 */ + #define NFS4_DEBUG 1 /* Index of predefined Linux client operations */ -- cgit v1.2.3 From 3fd5be9e19921a89d9ed78d6a708a379a6c3c76a Mon Sep 17 00:00:00 2001 From: Mike Sager Date: Wed, 1 Apr 2009 09:21:48 -0400 Subject: nfs41: add mount command option minorversion mount -t nfs4 -o minorversion=[0|1] specifies whether to use 4.0 or 4.1. By default, the minorversion is set to 0. Signed-off-by: Mike Sager [set default minorversion to 0 as per Trond and SteveD's request] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/internal.h | 1 + fs/nfs/super.c | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index e4d6a8348ad..ffa6bd54d43 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -44,6 +44,7 @@ struct nfs_parsed_mount_data { unsigned int auth_flavor_len; rpc_authflavor_t auth_flavors[1]; char *client_address; + unsigned int minorversion; char *fscache_uniq; struct { diff --git a/fs/nfs/super.c b/fs/nfs/super.c index d2d67781c57..5a8fdc791cc 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -90,6 +90,7 @@ enum { Opt_mountport, Opt_mountvers, Opt_nfsvers, + Opt_minorversion, /* Mount options that take string arguments */ Opt_sec, Opt_proto, Opt_mountproto, Opt_mounthost, @@ -155,6 +156,7 @@ static const match_table_t nfs_mount_option_tokens = { { Opt_mountvers, "mountvers=%u" }, { Opt_nfsvers, "nfsvers=%u" }, { Opt_nfsvers, "vers=%u" }, + { Opt_minorversion, "minorversion=%u" }, { Opt_sec, "sec=%s" }, { Opt_proto, "proto=%s" }, @@ -1211,6 +1213,13 @@ static int nfs_parse_mount_options(char *raw, nfs_parse_invalid_value("nfsvers"); } break; + case Opt_minorversion: + if (match_int(args, &option)) + return 0; + if (option < 0 || option > NFS4_MAX_MINOR_VERSION) + return 0; + mnt->minorversion = option; + break; /* * options that take text values @@ -2261,6 +2270,7 @@ static int nfs4_validate_mount_data(void *options, args->nfs_server.port = NFS_PORT; /* 2049 unless user set port= */ args->auth_flavors[0] = RPC_AUTH_UNIX; args->auth_flavor_len = 0; + args->minorversion = 0; switch (data->version) { case 1: -- cgit v1.2.3 From 94a417f3d7a02478a2d7842e693a61339fb54ea4 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:21:49 -0400 Subject: nfs41: nfs_client.cl_minorversion This field is set to the nfsv4 minor version for this mount. Signed-off-by: Benny Halevy Note: This patch sets the referral to the same minorversion as the current mount. Revisit in future patch. Signed-off-by: Andy Adamson [removed cl_minorversion assignment in nfs_set_client] Signed-off-by: Benny Halevy [always define nfs_client.cl_minorversion] Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 9 ++++++--- include/linux/nfs_fs_sb.h | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 75c9cd2aa11..0efcb55c2ca 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1101,7 +1101,8 @@ static int nfs4_set_client(struct nfs_server *server, const size_t addrlen, const char *ip_addr, rpc_authflavor_t authflavour, - int proto, const struct rpc_timeout *timeparms) + int proto, const struct rpc_timeout *timeparms, + u32 minorversion) { struct nfs_client_initdata cl_init = { .hostname = hostname, @@ -1164,7 +1165,8 @@ static int nfs4_init_server(struct nfs_server *server, data->client_address, data->auth_flavors[0], data->nfs_server.protocol, - &timeparms); + &timeparms, + data->minorversion); if (error < 0) goto error; @@ -1282,7 +1284,8 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, parent_client->cl_ipaddr, data->authflavor, parent_server->client->cl_xprt->prot, - parent_server->client->cl_timeout); + parent_server->client->cl_timeout, + parent_client->cl_minorversion); if (error < 0) goto error; diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 6ad75948cbf..e9a51fe46aa 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -32,6 +32,7 @@ struct nfs_client { const struct nfs_rpc_ops *rpc_ops; /* NFS protocol vector */ int cl_proto; /* Network transport protocol */ + u32 cl_minorversion;/* NFSv4 minorversion */ struct rpc_cred *cl_machine_cred; #ifdef CONFIG_NFS_V4 @@ -63,7 +64,7 @@ struct nfs_client { */ char cl_ipaddr[48]; unsigned char cl_id_uniquifier; -#endif +#endif /* CONFIG_NFS_V4 */ #ifdef CONFIG_NFS_FSCACHE struct fscache_cookie *fscache; /* client index cache cookie */ -- cgit v1.2.3 From 5aae4a9ae0dd55409a42ca61b82ef1f5a840091e Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:21:50 -0400 Subject: nfs41: Use mount minorversion option Use the mount minorversion option to initialize the nfs_client cl_minorversion and match it in nfs_match_client() when looking up a nfs_client. [nfs41: remove ifdefs around nfs_client_initdata.minorversion] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 0efcb55c2ca..a736160046c 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -102,6 +102,7 @@ struct nfs_client_initdata { size_t addrlen; const struct nfs_rpc_ops *rpc_ops; int proto; + u32 minorversion; }; /* @@ -150,6 +151,7 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_ rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); clp->cl_boot_time = CURRENT_TIME; clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED; + clp->cl_minorversion = cl_init->minorversion; #endif cred = rpc_lookup_machine_cred(); if (!IS_ERR(cred)) @@ -420,7 +422,9 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat if (clp->cl_proto != data->proto) continue; - + /* Match nfsv4 minorversion */ + if (clp->cl_minorversion != data->minorversion) + continue; /* Match the full socket address */ if (!nfs_sockaddr_cmp(sap, clap)) continue; @@ -1110,6 +1114,7 @@ static int nfs4_set_client(struct nfs_server *server, .addrlen = addrlen, .rpc_ops = &nfs_v4_clientops, .proto = proto, + .minorversion = minorversion, }; struct nfs_client *clp; int error; -- cgit v1.2.3 From c2e713dd83dcabb2ae951ea572e7de68c96e2d48 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:21:51 -0400 Subject: nfs41: translate NFS4ERR_MINOR_VERS_MISMATCH to EPROTONOSUPPORT To be returned to the mount command when trying to mount a v4 server using minorversion 1. Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4state.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 0298e909559..bc683ed477e 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1107,6 +1107,8 @@ static int nfs4_reclaim_lease(struct nfs_client *clp) nfs4_clear_machine_cred(clp); status = -EAGAIN; } + if (status == -NFS4ERR_MINOR_VERS_MISMATCH) + status = -EPROTONOSUPPORT; } return status; } -- cgit v1.2.3 From 9ff71c3a9827b99699510076dffa0bbe7c36bfd4 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:21:52 -0400 Subject: nfs41: client xdr definitions Define stubs for sequence args and res data structures and embed them in all other nfs4 and nfs41 xdr types. They are needed for sending any op in a nfs41 compound rpc. Signed-off-by: Andy Adamson [moved new args/res definitions away, to where they're first used] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- include/linux/nfs_xdr.h | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index b89c34e40bc..f2c5700c7b6 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -145,6 +145,15 @@ struct nfs4_change_info { }; struct nfs_seqid; + +struct nfs4_sequence_args { + /* stub */ +}; + +struct nfs4_sequence_res { + /* stub */ +}; + /* * Arguments to the open call. */ @@ -165,6 +174,7 @@ struct nfs_openargs { const struct nfs_server *server; /* Needed for ID mapping */ const u32 * bitmask; __u32 claim; + struct nfs4_sequence_args seq_args; }; struct nfs_openres { @@ -181,6 +191,7 @@ struct nfs_openres { __u32 do_recall; __u64 maxsize; __u32 attrset[NFS4_BITMAP_SIZE]; + struct nfs4_sequence_res seq_res; }; /* @@ -206,6 +217,7 @@ struct nfs_closeargs { struct nfs_seqid * seqid; fmode_t fmode; const u32 * bitmask; + struct nfs4_sequence_args seq_args; }; struct nfs_closeres { @@ -213,6 +225,7 @@ struct nfs_closeres { struct nfs_fattr * fattr; struct nfs_seqid * seqid; const struct nfs_server *server; + struct nfs4_sequence_res seq_res; }; /* * * Arguments to the lock,lockt, and locku call. @@ -233,12 +246,14 @@ struct nfs_lock_args { unsigned char block : 1; unsigned char reclaim : 1; unsigned char new_lock_owner : 1; + struct nfs4_sequence_args seq_args; }; struct nfs_lock_res { nfs4_stateid stateid; struct nfs_seqid * lock_seqid; struct nfs_seqid * open_seqid; + struct nfs4_sequence_res seq_res; }; struct nfs_locku_args { @@ -246,32 +261,38 @@ struct nfs_locku_args { struct file_lock * fl; struct nfs_seqid * seqid; nfs4_stateid * stateid; + struct nfs4_sequence_args seq_args; }; struct nfs_locku_res { nfs4_stateid stateid; struct nfs_seqid * seqid; + struct nfs4_sequence_res seq_res; }; struct nfs_lockt_args { struct nfs_fh * fh; struct file_lock * fl; struct nfs_lowner lock_owner; + struct nfs4_sequence_args seq_args; }; struct nfs_lockt_res { struct file_lock * denied; /* LOCK, LOCKT failed */ + struct nfs4_sequence_res seq_res; }; struct nfs4_delegreturnargs { const struct nfs_fh *fhandle; const nfs4_stateid *stateid; const u32 * bitmask; + struct nfs4_sequence_args seq_args; }; struct nfs4_delegreturnres { struct nfs_fattr * fattr; const struct nfs_server *server; + struct nfs4_sequence_res seq_res; }; /* @@ -284,12 +305,14 @@ struct nfs_readargs { __u32 count; unsigned int pgbase; struct page ** pages; + struct nfs4_sequence_args seq_args; }; struct nfs_readres { struct nfs_fattr * fattr; __u32 count; int eof; + struct nfs4_sequence_res seq_res; }; /* @@ -304,6 +327,7 @@ struct nfs_writeargs { unsigned int pgbase; struct page ** pages; const u32 * bitmask; + struct nfs4_sequence_args seq_args; }; struct nfs_writeverf { @@ -316,6 +340,7 @@ struct nfs_writeres { struct nfs_writeverf * verf; __u32 count; const struct nfs_server *server; + struct nfs4_sequence_res seq_res; }; /* @@ -325,12 +350,14 @@ struct nfs_removeargs { const struct nfs_fh *fh; struct qstr name; const u32 * bitmask; + struct nfs4_sequence_args seq_args; }; struct nfs_removeres { const struct nfs_server *server; struct nfs4_change_info cinfo; struct nfs_fattr dir_attr; + struct nfs4_sequence_res seq_res; }; /* @@ -383,6 +410,7 @@ struct nfs_setattrargs { struct iattr * iap; const struct nfs_server * server; /* Needed for name mapping */ const u32 * bitmask; + struct nfs4_sequence_args seq_args; }; struct nfs_setaclargs { @@ -390,6 +418,7 @@ struct nfs_setaclargs { size_t acl_len; unsigned int acl_pgbase; struct page ** acl_pages; + struct nfs4_sequence_args seq_args; }; struct nfs_getaclargs { @@ -397,11 +426,13 @@ struct nfs_getaclargs { size_t acl_len; unsigned int acl_pgbase; struct page ** acl_pages; + struct nfs4_sequence_args seq_args; }; struct nfs_setattrres { struct nfs_fattr * fattr; const struct nfs_server * server; + struct nfs4_sequence_res seq_res; }; struct nfs_linkargs { @@ -583,6 +614,7 @@ struct nfs4_accessargs { const struct nfs_fh * fh; const u32 * bitmask; u32 access; + struct nfs4_sequence_args seq_args; }; struct nfs4_accessres { @@ -590,6 +622,7 @@ struct nfs4_accessres { struct nfs_fattr * fattr; u32 supported; u32 access; + struct nfs4_sequence_res seq_res; }; struct nfs4_create_arg { @@ -609,6 +642,7 @@ struct nfs4_create_arg { const struct iattr * attrs; const struct nfs_fh * dir_fh; const u32 * bitmask; + struct nfs4_sequence_args seq_args; }; struct nfs4_create_res { @@ -617,21 +651,25 @@ struct nfs4_create_res { struct nfs_fattr * fattr; struct nfs4_change_info dir_cinfo; struct nfs_fattr * dir_fattr; + struct nfs4_sequence_res seq_res; }; struct nfs4_fsinfo_arg { const struct nfs_fh * fh; const u32 * bitmask; + struct nfs4_sequence_args seq_args; }; struct nfs4_getattr_arg { const struct nfs_fh * fh; const u32 * bitmask; + struct nfs4_sequence_args seq_args; }; struct nfs4_getattr_res { const struct nfs_server * server; struct nfs_fattr * fattr; + struct nfs4_sequence_res seq_res; }; struct nfs4_link_arg { @@ -639,6 +677,7 @@ struct nfs4_link_arg { const struct nfs_fh * dir_fh; const struct qstr * name; const u32 * bitmask; + struct nfs4_sequence_args seq_args; }; struct nfs4_link_res { @@ -646,6 +685,7 @@ struct nfs4_link_res { struct nfs_fattr * fattr; struct nfs4_change_info cinfo; struct nfs_fattr * dir_attr; + struct nfs4_sequence_res seq_res; }; @@ -653,21 +693,25 @@ struct nfs4_lookup_arg { const struct nfs_fh * dir_fh; const struct qstr * name; const u32 * bitmask; + struct nfs4_sequence_args seq_args; }; struct nfs4_lookup_res { const struct nfs_server * server; struct nfs_fattr * fattr; struct nfs_fh * fh; + struct nfs4_sequence_res seq_res; }; struct nfs4_lookup_root_arg { const u32 * bitmask; + struct nfs4_sequence_args seq_args; }; struct nfs4_pathconf_arg { const struct nfs_fh * fh; const u32 * bitmask; + struct nfs4_sequence_args seq_args; }; struct nfs4_readdir_arg { @@ -678,11 +722,13 @@ struct nfs4_readdir_arg { struct page ** pages; /* zero-copy data */ unsigned int pgbase; /* zero-copy data */ const u32 * bitmask; + struct nfs4_sequence_args seq_args; }; struct nfs4_readdir_res { nfs4_verifier verifier; unsigned int pgbase; + struct nfs4_sequence_res seq_res; }; struct nfs4_readlink { @@ -690,6 +736,7 @@ struct nfs4_readlink { unsigned int pgbase; unsigned int pglen; /* zero-copy data */ struct page ** pages; /* zero-copy data */ + struct nfs4_sequence_args seq_args; }; struct nfs4_rename_arg { @@ -698,6 +745,7 @@ struct nfs4_rename_arg { const struct qstr * old_name; const struct qstr * new_name; const u32 * bitmask; + struct nfs4_sequence_args seq_args; }; struct nfs4_rename_res { @@ -706,6 +754,7 @@ struct nfs4_rename_res { struct nfs_fattr * old_fattr; struct nfs4_change_info new_cinfo; struct nfs_fattr * new_fattr; + struct nfs4_sequence_res seq_res; }; #define NFS4_SETCLIENTID_NAMELEN (127) @@ -724,6 +773,7 @@ struct nfs4_setclientid { struct nfs4_statfs_arg { const struct nfs_fh * fh; const u32 * bitmask; + struct nfs4_sequence_args seq_args; }; struct nfs4_server_caps_res { @@ -731,6 +781,7 @@ struct nfs4_server_caps_res { u32 acl_bitmask; u32 has_links; u32 has_symlinks; + struct nfs4_sequence_res seq_res; }; struct nfs4_string { @@ -765,6 +816,7 @@ struct nfs4_fs_locations_arg { const struct qstr *name; struct page *page; const u32 *bitmask; + struct nfs4_sequence_args seq_args; }; #endif /* CONFIG_NFS_V4 */ -- cgit v1.2.3 From 557134a39c8d2ab79d8b8d53438e03e29feb5ec4 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:21:53 -0400 Subject: nfs41: sessions client infrastructure NFSv4.1 Sessions basic data types, initialization, and destruction. The session is always associated with a struct nfs_client that holds the exchange_id results. Signed-off-by: Rahul Iyer Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [remove extraneous rpc_clnt pointer, use the struct nfs_client cl_rpcclient. remove the rpc_clnt parameter from nfs4 nfs4_init_session] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [Use the presence of a session to determine behaviour instead of the minorversion number.] Signed-off-by: Andy Adamson [constified nfs4_has_session's struct nfs_client parameter] Signed-off-by: Benny Halevy [Rename nfs4_put_session() to nfs4_destroy_session() and call it from nfs4_free_client() not nfs4_free_server(). Also get rid of nfs4_get_session() and the ref_count in nfs4_session struct as keeping track of nfs_client should be sufficient] Signed-off-by: Alexandros Batsakis [nfs41: pass rsize and wsize into nfs4_init_session] Signed-off-by: Andy Adamson [separated out removal of rpc_clnt parameter from nfs4_init_session ot a patch of its own] Signed-off-by: Benny Halevy [Pass the nfs_client pointer into nfs4_alloc_session] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: don't assign to session->clp->cl_session in nfs4_destroy_session] [nfs41: fixup nfs4_clear_client_minor_version] [introduce nfs4_clear_client_minor_version() in this patch] Signed-off-by: Benny Halevy [Refactor nfs4_init_session] Moved session allocation into nfs4_init_client_minor_version, called from nfs4_init_client. Leave rwise and wsize initialization in nfs4_init_session, called from nfs4_init_server. Reverted moving of nfs_fsid definition to nfs_fs_sb.h Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: Move NFS4_MAX_SLOT_TABLE define from under CONFIG_NFS_V4_1] [Fix comile error when CONFIG_NFS_V4_1 is not set.] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [moved nfs4_init_slot_table definition to "create_session operation"] Signed-off-by: Benny Halevy [nfs41: alloc session with GFP_KERNEL] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++ fs/nfs/internal.h | 12 ++++++++++ fs/nfs/nfs4_fs.h | 4 ++++ fs/nfs/nfs4proc.c | 34 +++++++++++++++++++++++++++ include/linux/nfs_fs_sb.h | 49 ++++++++++++++++++++++++++++++++++++++ include/linux/nfs_xdr.h | 15 ++++++++++++ 6 files changed, 174 insertions(+) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index a736160046c..f1506f14852 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -183,6 +183,20 @@ static void nfs4_shutdown_client(struct nfs_client *clp) #endif } +/* + * Clears/puts all minor version specific parts from an nfs_client struct + * reverting it to minorversion 0. + */ +static void nfs4_clear_client_minor_version(struct nfs_client *clp) +{ +#ifdef CONFIG_NFS_V4_1 + if (nfs4_has_session(clp)) { + nfs4_destroy_session(clp->cl_session); + clp->cl_session = NULL; + } +#endif /* CONFIG_NFS_V4_1 */ +} + /* * Destroy a shared client record */ @@ -190,6 +204,7 @@ static void nfs_free_client(struct nfs_client *clp) { dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version); + nfs4_clear_client_minor_version(clp); nfs4_shutdown_client(clp); nfs_fscache_release_client_cookie(clp); @@ -1053,6 +1068,30 @@ error: } #ifdef CONFIG_NFS_V4 +/* + * Initialize the minor version specific parts of an NFS4 client record + */ +static int nfs4_init_client_minor_version(struct nfs_client *clp) +{ +#if defined(CONFIG_NFS_V4_1) + if (clp->cl_minorversion) { + struct nfs4_session *session = NULL; + /* + * Create the session and mark it expired. + * When a SEQUENCE operation encounters the expired session + * it will do session recovery to initialize it. + */ + session = nfs4_alloc_session(clp); + if (!session) + return -ENOMEM; + + clp->cl_session = session; + } +#endif /* CONFIG_NFS_V4_1 */ + + return 0; +} + /* * Initialise an NFS4 client record */ @@ -1087,6 +1126,10 @@ static int nfs4_init_client(struct nfs_client *clp, } __set_bit(NFS_CS_IDMAP, &clp->cl_res_state); + error = nfs4_init_client_minor_version(clp); + if (error < 0) + goto error; + nfs_mark_client_ready(clp, NFS_CS_READY); return 0; @@ -1143,6 +1186,21 @@ error: return error; } +/* + * Initialize a session. + * Note: save the mount rsize and wsize for create_server negotiation. + */ +static void nfs4_init_session(struct nfs_client *clp, + unsigned int wsize, unsigned int rsize) +{ +#if defined(CONFIG_NFS_V4_1) + if (nfs4_has_session(clp)) { + clp->cl_session->fc_attrs.max_rqst_sz = wsize; + clp->cl_session->fc_attrs.max_resp_sz = rsize; + } +#endif /* CONFIG_NFS_V4_1 */ +} + /* * Create a version 4 volume record */ @@ -1221,6 +1279,8 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, BUG_ON(!server->nfs_client->rpc_ops); BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); + nfs4_init_session(server->nfs_client, server->wsize, server->rsize); + /* Probe the root fh to retrieve its FSID */ error = nfs4_path_walk(server, mntfh, data->nfs_server.export_path); if (error < 0) diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index ffa6bd54d43..7cef45db925 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -206,6 +206,18 @@ extern int nfs4_path_walk(struct nfs_server *server, const char *path); #endif +/* + * Determine if sessions are in use. + */ +static inline int nfs4_has_session(const struct nfs_client *clp) +{ +#ifdef CONFIG_NFS_V4_1 + if (clp->cl_session) + return 1; +#endif /* CONFIG_NFS_V4_1 */ + return 0; +} + /* * Determine the device name as a string */ diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 84345deab26..acac6f8c3d3 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -202,6 +202,10 @@ extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, extern struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops; extern struct nfs4_state_recovery_ops nfs4_nograce_recovery_ops; +#if defined(CONFIG_NFS_V4_1) +extern void nfs4_destroy_session(struct nfs4_session *session); +extern struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp); +#endif /* CONFIG_NFS_V4_1 */ extern const u32 nfs4_fattr_bitmap[2]; extern const u32 nfs4_statfs_bitmap[2]; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 4674f8092da..cdd8e74c47d 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3723,6 +3723,40 @@ int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, return status; } +#ifdef CONFIG_NFS_V4_1 +/* Destroy the slot table */ +static void nfs4_destroy_slot_table(struct nfs4_session *session) +{ + if (session->fc_slot_table.slots == NULL) + return; + kfree(session->fc_slot_table.slots); + session->fc_slot_table.slots = NULL; + return; +} + +struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp) +{ + struct nfs4_session *session; + struct nfs4_slot_table *tbl; + + session = kzalloc(sizeof(struct nfs4_session), GFP_KERNEL); + if (!session) + return NULL; + tbl = &session->fc_slot_table; + spin_lock_init(&tbl->slot_tbl_lock); + rpc_init_wait_queue(&tbl->slot_tbl_waitq, "Slot table"); + session->clp = clp; + return session; +} + +void nfs4_destroy_session(struct nfs4_session *session) +{ + nfs4_destroy_slot_table(session); + kfree(session); +} + +#endif /* CONFIG_NFS_V4_1 */ + struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops = { .owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT, .state_flag_bit = NFS_STATE_RECLAIM_REBOOT, diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index e9a51fe46aa..b47c0fc55d4 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -4,9 +4,12 @@ #include #include #include +#include +#include #include +struct nfs4_session; struct nfs_iostats; struct nlm_host; @@ -66,6 +69,10 @@ struct nfs_client { unsigned char cl_id_uniquifier; #endif /* CONFIG_NFS_V4 */ +#ifdef CONFIG_NFS_V4_1 + struct nfs4_session *cl_session; /* sharred session */ +#endif /* CONFIG_NFS_V4_1 */ + #ifdef CONFIG_NFS_FSCACHE struct fscache_cookie *fscache; /* client index cache cookie */ #endif @@ -146,4 +153,46 @@ struct nfs_server { #define NFS_CAP_ACLS (1U << 3) #define NFS_CAP_ATOMIC_OPEN (1U << 4) + +/* maximum number of slots to use */ +#define NFS4_MAX_SLOT_TABLE RPC_MAX_SLOT_TABLE + +#if defined(CONFIG_NFS_V4_1) + +/* Sessions */ +#define SLOT_TABLE_SZ (NFS4_MAX_SLOT_TABLE/(8*sizeof(long))) +struct nfs4_slot_table { + struct nfs4_slot *slots; /* seqid per slot */ + unsigned long used_slots[SLOT_TABLE_SZ]; /* used/unused bitmap */ + spinlock_t slot_tbl_lock; + struct rpc_wait_queue slot_tbl_waitq; /* allocators may wait here */ + int max_slots; /* # slots in table */ + int highest_used_slotid; /* sent to server on each SEQ. + * op for dynamic resizing */ +}; + +static inline int slot_idx(struct nfs4_slot_table *tbl, struct nfs4_slot *sp) +{ + return sp - tbl->slots; +} + +/* + * Session related parameters + */ +struct nfs4_session { + struct nfs4_sessionid sess_id; + u32 flags; + unsigned long session_state; + u32 hash_alg; + u32 ssv_len; + + /* The fore and back channel */ + struct nfs4_channel_attrs fc_attrs; + struct nfs4_slot_table fc_slot_table; + struct nfs4_channel_attrs bc_attrs; + /* back channel has one slot */ + struct nfs_client *clp; +}; + +#endif /* CONFIG_NFS_V4_1 */ #endif diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index f2c5700c7b6..8f8c026c558 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -146,6 +146,21 @@ struct nfs4_change_info { struct nfs_seqid; +/* nfs41 sessions channel attributes */ +struct nfs4_channel_attrs { + u32 headerpadsz; + u32 max_rqst_sz; + u32 max_resp_sz; + u32 max_resp_sz_cached; + u32 max_ops; + u32 max_reqs; +}; + +/* nfs41 sessions slot seqid */ +struct nfs4_slot { + u32 seq_nr; +}; + struct nfs4_sequence_args { /* stub */ }; -- cgit v1.2.3 From 43652ad55342d9146d8035932101a5814b22315a Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:21:54 -0400 Subject: nfs41: use nfs4_server_caps_arg In preparation for nfs41 sequence processing. Signed-off-by: Andy Admason [define nfs4_server_caps_arg] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 5 ++++- fs/nfs/nfs4xdr.c | 5 +++-- include/linux/nfs_xdr.h | 5 +++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index cdd8e74c47d..5ef1022b7e6 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1584,10 +1584,13 @@ void nfs4_close_context(struct nfs_open_context *ctx, int is_sync) static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle) { + struct nfs4_server_caps_arg args = { + .fhandle = fhandle, + }; struct nfs4_server_caps_res res = {}; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SERVER_CAPS], - .rpc_argp = fhandle, + .rpc_argp = &args, .rpc_resp = &res, }; int status; diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 1690f0e44b9..91305861037 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -1900,7 +1900,8 @@ static int nfs4_xdr_enc_statfs(struct rpc_rqst *req, __be32 *p, const struct nfs /* * GETATTR_BITMAP request */ -static int nfs4_xdr_enc_server_caps(struct rpc_rqst *req, __be32 *p, const struct nfs_fh *fhandle) +static int nfs4_xdr_enc_server_caps(struct rpc_rqst *req, __be32 *p, + struct nfs4_server_caps_arg *args) { struct xdr_stream xdr; struct compound_hdr hdr = { @@ -1909,7 +1910,7 @@ static int nfs4_xdr_enc_server_caps(struct rpc_rqst *req, __be32 *p, const struc xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, &hdr); - encode_putfh(&xdr, fhandle, &hdr); + encode_putfh(&xdr, args->fhandle, &hdr); encode_getattr_one(&xdr, FATTR4_WORD0_SUPPORTED_ATTRS| FATTR4_WORD0_LINK_SUPPORT| FATTR4_WORD0_SYMLINK_SUPPORT| diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 8f8c026c558..a7b7f2a059c 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -791,6 +791,11 @@ struct nfs4_statfs_arg { struct nfs4_sequence_args seq_args; }; +struct nfs4_server_caps_arg { + struct nfs_fh *fhandle; + struct nfs4_sequence_args seq_args; +}; + struct nfs4_server_caps_res { u32 attr_bitmask[2]; u32 acl_bitmask; -- cgit v1.2.3 From f50c7000817e7cb4e676ac5d911a82c0f3fd226f Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:21:55 -0400 Subject: nfs41: use nfs4_readlink_res In preparation for nfs41 sequence processing. Signed-off-by: Andy Admason [define nfs4_readlink_res] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 3 ++- fs/nfs/nfs4xdr.c | 3 ++- include/linux/nfs_xdr.h | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 5ef1022b7e6..b0ec8ff96eb 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1960,10 +1960,11 @@ static int _nfs4_proc_readlink(struct inode *inode, struct page *page, .pglen = pglen, .pages = &page, }; + struct nfs4_readlink_res res; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READLINK], .rpc_argp = &args, - .rpc_resp = NULL, + .rpc_resp = &res, }; return rpc_call_sync(NFS_CLIENT(inode), &msg, 0); diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 91305861037..1e41420916a 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -4235,7 +4235,8 @@ out: /* * Decode READLINK response */ -static int nfs4_xdr_dec_readlink(struct rpc_rqst *rqstp, __be32 *p, void *res) +static int nfs4_xdr_dec_readlink(struct rpc_rqst *rqstp, __be32 *p, + struct nfs4_readlink_res *res) { struct xdr_stream xdr; struct compound_hdr hdr; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index a7b7f2a059c..f71260aeb80 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -754,6 +754,10 @@ struct nfs4_readlink { struct nfs4_sequence_args seq_args; }; +struct nfs4_readlink_res { + struct nfs4_sequence_res seq_res; +}; + struct nfs4_rename_arg { const struct nfs_fh * old_dir; const struct nfs_fh * new_dir; -- cgit v1.2.3 From 24ad148a0ff74b1e703a8bc5b3e0793dc7d4e3a9 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:21:56 -0400 Subject: nfs41: use nfs4_statfs_res In preparation for nfs41 sequence processing. Signed-off-by: Andy Admason [define nfs4_statfs_res] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 5 ++++- fs/nfs/nfs4xdr.c | 5 +++-- include/linux/nfs_xdr.h | 5 +++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index b0ec8ff96eb..b715f605761 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2426,10 +2426,13 @@ static int _nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, .fh = fhandle, .bitmask = server->attr_bitmask, }; + struct nfs4_statfs_res res = { + .fsstat = fsstat, + }; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_STATFS], .rpc_argp = &args, - .rpc_resp = fsstat, + .rpc_resp = &res, }; nfs_fattr_init(fsstat->fattr); diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 1e41420916a..b7871ad82aa 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -4387,7 +4387,8 @@ static int nfs4_xdr_dec_pathconf(struct rpc_rqst *req, __be32 *p, struct nfs_pat /* * STATFS request */ -static int nfs4_xdr_dec_statfs(struct rpc_rqst *req, __be32 *p, struct nfs_fsstat *fsstat) +static int nfs4_xdr_dec_statfs(struct rpc_rqst *req, __be32 *p, + struct nfs4_statfs_res *res) { struct xdr_stream xdr; struct compound_hdr hdr; @@ -4398,7 +4399,7 @@ static int nfs4_xdr_dec_statfs(struct rpc_rqst *req, __be32 *p, struct nfs_fssta if (!status) status = decode_putfh(&xdr); if (!status) - status = decode_statfs(&xdr, fsstat); + status = decode_statfs(&xdr, res->fsstat); return status; } diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index f71260aeb80..4dac59ef6f4 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -795,6 +795,11 @@ struct nfs4_statfs_arg { struct nfs4_sequence_args seq_args; }; +struct nfs4_statfs_res { + struct nfs_fsstat *fsstat; + struct nfs4_sequence_res seq_res; +}; + struct nfs4_server_caps_arg { struct nfs_fh *fhandle; struct nfs4_sequence_args seq_args; -- cgit v1.2.3 From 3dda5e434721f942870ee30bc6103761618d410f Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:21:57 -0400 Subject: nfs41: use nfs4_fsinfo_res In preparation for nfs41 sequence processing. Signed-off-by: Andy Admason [define nfs4_fsinfo_res] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 5 ++++- fs/nfs/nfs4xdr.c | 5 +++-- include/linux/nfs_xdr.h | 5 +++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index b715f605761..b8915ef533d 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2458,10 +2458,13 @@ static int _nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, .fh = fhandle, .bitmask = server->attr_bitmask, }; + struct nfs4_fsinfo_res res = { + .fsinfo = fsinfo, + }; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FSINFO], .rpc_argp = &args, - .rpc_resp = fsinfo, + .rpc_resp = &res, }; return rpc_call_sync(server->client, &msg, 0); diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index b7871ad82aa..d9ab8209c28 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -4351,7 +4351,8 @@ out: /* * FSINFO request */ -static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *fsinfo) +static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, __be32 *p, + struct nfs4_fsinfo_res *res) { struct xdr_stream xdr; struct compound_hdr hdr; @@ -4362,7 +4363,7 @@ static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, __be32 *p, struct nfs_fsinf if (!status) status = decode_putfh(&xdr); if (!status) - status = decode_fsinfo(&xdr, fsinfo); + status = decode_fsinfo(&xdr, res->fsinfo); return status; } diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 4dac59ef6f4..7d64913cbb1 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -675,6 +675,11 @@ struct nfs4_fsinfo_arg { struct nfs4_sequence_args seq_args; }; +struct nfs4_fsinfo_res { + struct nfs_fsinfo *fsinfo; + struct nfs4_sequence_res seq_res; +}; + struct nfs4_getattr_arg { const struct nfs_fh * fh; const u32 * bitmask; -- cgit v1.2.3 From d45b2989a7956ae9e71d584ceac942278c0371c7 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:21:58 -0400 Subject: nfs41: use nfs4_pathconf_res In preparation for nfs41 sequence processing. Signed-off-by: Andy Admason [define nfs4_pathconf_res] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 5 ++++- fs/nfs/nfs4xdr.c | 5 +++-- include/linux/nfs_xdr.h | 5 +++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index b8915ef533d..aea2e83d393 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2496,10 +2496,13 @@ static int _nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle .fh = fhandle, .bitmask = server->attr_bitmask, }; + struct nfs4_pathconf_res res = { + .pathconf = pathconf, + }; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_PATHCONF], .rpc_argp = &args, - .rpc_resp = pathconf, + .rpc_resp = &res, }; /* None of the pathconf attributes are mandatory to implement */ diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index d9ab8209c28..a77ee3dd0b3 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -4370,7 +4370,8 @@ static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, __be32 *p, /* * PATHCONF request */ -static int nfs4_xdr_dec_pathconf(struct rpc_rqst *req, __be32 *p, struct nfs_pathconf *pathconf) +static int nfs4_xdr_dec_pathconf(struct rpc_rqst *req, __be32 *p, + struct nfs4_pathconf_res *res) { struct xdr_stream xdr; struct compound_hdr hdr; @@ -4381,7 +4382,7 @@ static int nfs4_xdr_dec_pathconf(struct rpc_rqst *req, __be32 *p, struct nfs_pat if (!status) status = decode_putfh(&xdr); if (!status) - status = decode_pathconf(&xdr, pathconf); + status = decode_pathconf(&xdr, res->pathconf); return status; } diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 7d64913cbb1..56523319e14 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -734,6 +734,11 @@ struct nfs4_pathconf_arg { struct nfs4_sequence_args seq_args; }; +struct nfs4_pathconf_res { + struct nfs_pathconf *pathconf; + struct nfs4_sequence_res seq_res; +}; + struct nfs4_readdir_arg { const struct nfs_fh * fh; u64 cookie; -- cgit v1.2.3 From 663c79b3cd8f5fe21fe7d7565fec0072e3234ddc Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:21:59 -0400 Subject: nfs41: use nfs4_getaclres In preparation for nfs41 sequence processing. Signed-off-by: Andy Admason Signed-off-by: Benny Halevy [nfs41: embed resp_len in nfs_getaclres] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 20 +++++++++++--------- fs/nfs/nfs4xdr.c | 5 +++-- include/linux/nfs_xdr.h | 5 +++++ 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index aea2e83d393..20c9acf689f 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2755,12 +2755,14 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu .acl_pages = pages, .acl_len = buflen, }; - size_t resp_len = buflen; + struct nfs_getaclres res = { + .acl_len = buflen, + }; void *resp_buf; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETACL], .rpc_argp = &args, - .rpc_resp = &resp_len, + .rpc_resp = &res, }; struct page *localpage = NULL; int ret; @@ -2774,7 +2776,7 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu return -ENOMEM; args.acl_pages[0] = localpage; args.acl_pgbase = 0; - resp_len = args.acl_len = PAGE_SIZE; + args.acl_len = PAGE_SIZE; } else { resp_buf = buf; buf_to_pages(buf, buflen, args.acl_pages, &args.acl_pgbase); @@ -2782,18 +2784,18 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu ret = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); if (ret) goto out_free; - if (resp_len > args.acl_len) - nfs4_write_cached_acl(inode, NULL, resp_len); + if (res.acl_len > args.acl_len) + nfs4_write_cached_acl(inode, NULL, res.acl_len); else - nfs4_write_cached_acl(inode, resp_buf, resp_len); + nfs4_write_cached_acl(inode, resp_buf, res.acl_len); if (buf) { ret = -ERANGE; - if (resp_len > buflen) + if (res.acl_len > buflen) goto out_free; if (localpage) - memcpy(buf, resp_buf, resp_len); + memcpy(buf, resp_buf, res.acl_len); } - ret = resp_len; + ret = res.acl_len; out_free: if (localpage) __free_page(localpage); diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index a77ee3dd0b3..3e777893e2b 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -4018,7 +4018,8 @@ out: * Decode GETACL response */ static int -nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, __be32 *p, size_t *acl_len) +nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, __be32 *p, + struct nfs_getaclres *res) { struct xdr_stream xdr; struct compound_hdr hdr; @@ -4031,7 +4032,7 @@ nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, __be32 *p, size_t *acl_len) status = decode_putfh(&xdr); if (status) goto out; - status = decode_getacl(&xdr, rqstp, acl_len); + status = decode_getacl(&xdr, rqstp, &res->acl_len); out: return status; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 56523319e14..6e9ee284860 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -444,6 +444,11 @@ struct nfs_getaclargs { struct nfs4_sequence_args seq_args; }; +struct nfs_getaclres { + size_t acl_len; + struct nfs4_sequence_res seq_res; +}; + struct nfs_setattrres { struct nfs_fattr * fattr; const struct nfs_server * server; -- cgit v1.2.3 From 9e9ecc03d6b37a5d85e16b357751e3b341de0fef Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:22:00 -0400 Subject: NFS: get rid of unused xdr decode_setattr(, res) argument Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4xdr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 3e777893e2b..27dd25d9ad4 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -3658,7 +3658,7 @@ decode_savefh(struct xdr_stream *xdr) return decode_op_hdr(xdr, OP_SAVEFH); } -static int decode_setattr(struct xdr_stream *xdr, struct nfs_setattrres *res) +static int decode_setattr(struct xdr_stream *xdr) { __be32 *p; uint32_t bmlen; @@ -4009,7 +4009,7 @@ nfs4_xdr_dec_setacl(struct rpc_rqst *rqstp, __be32 *p, void *res) status = decode_putfh(&xdr); if (status) goto out; - status = decode_setattr(&xdr, res); + status = decode_setattr(&xdr); out: return status; } @@ -4162,7 +4162,7 @@ static int nfs4_xdr_dec_setattr(struct rpc_rqst *rqstp, __be32 *p, struct nfs_se status = decode_putfh(&xdr); if (status) goto out; - status = decode_setattr(&xdr, res); + status = decode_setattr(&xdr); if (status) goto out; decode_getfattr(&xdr, res->fattr, res->server); -- cgit v1.2.3 From 73c403a9a93743b068103c13c05ed136dc687d05 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:22:01 -0400 Subject: nfs41: use nfs4_setaclres In preparation for nfs41 sequence processing. Signed-off-by: Andy Admason [define nfs_setaclres] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 3 ++- fs/nfs/nfs4xdr.c | 3 ++- include/linux/nfs_xdr.h | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 20c9acf689f..62bbe25d942 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2842,10 +2842,11 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl .acl_pages = pages, .acl_len = buflen, }; + struct nfs_setaclres res; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETACL], .rpc_argp = &arg, - .rpc_resp = NULL, + .rpc_resp = &res, }; int ret; diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 27dd25d9ad4..aa350d5bf20 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -3996,7 +3996,8 @@ nfs4_xdr_enc_setacl(struct rpc_rqst *req, __be32 *p, struct nfs_setaclargs *args * Decode SETACL response */ static int -nfs4_xdr_dec_setacl(struct rpc_rqst *rqstp, __be32 *p, void *res) +nfs4_xdr_dec_setacl(struct rpc_rqst *rqstp, __be32 *p, + struct nfs_setaclres *res) { struct xdr_stream xdr; struct compound_hdr hdr; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 6e9ee284860..0f2dc8f4cc3 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -436,6 +436,10 @@ struct nfs_setaclargs { struct nfs4_sequence_args seq_args; }; +struct nfs_setaclres { + struct nfs4_sequence_res seq_res; +}; + struct nfs_getaclargs { struct nfs_fh * fh; size_t acl_len; -- cgit v1.2.3 From 22958463d5dca8548e19430779f379e66fd6e4a4 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:22:02 -0400 Subject: nfs41: use nfs4_fs_locations_res In preparation for nfs41 sequence processing. Signed-off-by: Andy Admason [find nfs4_fs_locations_res] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 5 ++++- fs/nfs/nfs4xdr.c | 6 ++++-- include/linux/nfs_xdr.h | 5 +++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 62bbe25d942..e08edc99faa 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3722,10 +3722,13 @@ int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, .page = page, .bitmask = bitmask, }; + struct nfs4_fs_locations_res res = { + .fs_locations = fs_locations, + }; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS], .rpc_argp = &args, - .rpc_resp = fs_locations, + .rpc_resp = &res, }; int status; diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index aa350d5bf20..e448e33b4d0 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -4504,7 +4504,8 @@ out: /* * FS_LOCATIONS request */ -static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req, __be32 *p, struct nfs4_fs_locations *res) +static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req, __be32 *p, + struct nfs4_fs_locations_res *res) { struct xdr_stream xdr; struct compound_hdr hdr; @@ -4519,7 +4520,8 @@ static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req, __be32 *p, struct nfs if ((status = decode_lookup(&xdr)) != 0) goto out; xdr_enter_page(&xdr, PAGE_SIZE); - status = decode_getfattr(&xdr, &res->fattr, res->server); + status = decode_getfattr(&xdr, &res->fs_locations->fattr, + res->fs_locations->server); out: return status; } diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 0f2dc8f4cc3..d837f10c49e 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -867,6 +867,11 @@ struct nfs4_fs_locations_arg { struct nfs4_sequence_args seq_args; }; +struct nfs4_fs_locations_res { + struct nfs4_fs_locations *fs_locations; + struct nfs4_sequence_res seq_res; +}; + #endif /* CONFIG_NFS_V4 */ struct nfs_page; -- cgit v1.2.3 From cccef3b96a4759ae0790452280c00ea505412157 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:03 -0400 Subject: nfs41: introduce nfs4_call_sync Use nfs4_call_sync rather than rpc_call_sync to provide for a nfs41 sessions-enabled interface for sessions manipulation. The nfs41 rpc logic uses the rpc_call_prepare method to recover and create the session, as well as selecting a free slot id and the rpc_call_done to free the slot and update slot table related metadata. In the coming patches we'll add rpc prepare and done routines for setting up the sequence op and processing the sequence result. Signed-off-by: Benny Halevy [nfs41: nfs4_call_sync] As per 11-14-08 review. Squash into "nfs41: introduce nfs4_call_sync" and "nfs41: nfs4_setup_sequence" Define two functions one for v4 and one for v41 add a pointer to struct nfs4_client to the correct one. Signed-off-by: Andy Adamson [added BUG() in _nfs4_call_sync_session if !CONFIG_NFS_V4_1] Signed-off-by: Benny Halevy [nfs41: check for session not minorversion] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [group minorversion specific stuff together] Signed-off-by: Alexandros Batsakis Signed-off-by: Benny Halevy Signed-off-by: Andy Adamson [nfs41: fixup nfs4_clear_client_minor_version] [introduce nfs4_init_client_minor_version() in this patch] Signed-off-by: Benny Halevy [cleaned-up patch: got rid of nfs_call_sync_t, dprintks, cosmetics, extra server defs] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 5 ++++ fs/nfs/internal.h | 12 +++++++++ fs/nfs/nfs4proc.c | 67 +++++++++++++++++++++++++++++++++-------------- include/linux/nfs_fs_sb.h | 8 ++++++ 4 files changed, 73 insertions(+), 19 deletions(-) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index f1506f14852..a9828baaa44 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -194,6 +194,8 @@ static void nfs4_clear_client_minor_version(struct nfs_client *clp) nfs4_destroy_session(clp->cl_session); clp->cl_session = NULL; } + + clp->cl_call_sync = _nfs4_call_sync; #endif /* CONFIG_NFS_V4_1 */ } @@ -1073,6 +1075,8 @@ error: */ static int nfs4_init_client_minor_version(struct nfs_client *clp) { + clp->cl_call_sync = _nfs4_call_sync; + #if defined(CONFIG_NFS_V4_1) if (clp->cl_minorversion) { struct nfs4_session *session = NULL; @@ -1086,6 +1090,7 @@ static int nfs4_init_client_minor_version(struct nfs_client *clp) return -ENOMEM; clp->cl_session = session; + clp->cl_call_sync = _nfs4_call_sync_session; } #endif /* CONFIG_NFS_V4_1 */ diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 7cef45db925..8d67c2865dc 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -206,6 +206,18 @@ extern int nfs4_path_walk(struct nfs_server *server, const char *path); #endif +/* nfs4proc.c */ +extern int _nfs4_call_sync(struct nfs_server *server, + struct rpc_message *msg, + struct nfs4_sequence_args *args, + struct nfs4_sequence_res *res, + int cache_reply); +extern int _nfs4_call_sync_session(struct nfs_server *server, + struct rpc_message *msg, + struct nfs4_sequence_args *args, + struct nfs4_sequence_res *res, + int cache_reply); + /* * Determine if sessions are in use. */ diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index e08edc99faa..4fc5b385f61 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -271,6 +271,33 @@ static void renew_lease(const struct nfs_server *server, unsigned long timestamp spin_unlock(&clp->cl_lock); } +#if defined(CONFIG_NFS_V4_1) + +int _nfs4_call_sync_session(struct nfs_server *server, + struct rpc_message *msg, + struct nfs4_sequence_args *args, + struct nfs4_sequence_res *res, + int cache_reply) +{ + /* in preparation for setting up the sequence op */ + return rpc_call_sync(server->client, msg, 0); +} + +#endif /* CONFIG_NFS_V4_1 */ + +int _nfs4_call_sync(struct nfs_server *server, + struct rpc_message *msg, + struct nfs4_sequence_args *args, + struct nfs4_sequence_res *res, + int cache_reply) +{ + return rpc_call_sync(server->client, msg, 0); +} + +#define nfs4_call_sync(server, msg, args, res, cache_reply) \ + (server)->nfs_client->cl_call_sync((server), (msg), &(args)->seq_args, \ + &(res)->seq_res, (cache_reply)) + static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo) { struct nfs_inode *nfsi = NFS_I(dir); @@ -1269,7 +1296,7 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, } else memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid)); - status = rpc_call_sync(server->client, &msg, 0); + status = nfs4_call_sync(server, &msg, &arg, &res, 1); if (status == 0 && state != NULL) renew_lease(server, timestamp); return status; @@ -1595,7 +1622,7 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f }; int status; - status = rpc_call_sync(server->client, &msg, 0); + status = nfs4_call_sync(server, &msg, &args, &res, 0); if (status == 0) { memcpy(server->attr_bitmask, res.attr_bitmask, sizeof(server->attr_bitmask)); if (res.attr_bitmask[0] & FATTR4_WORD0_ACL) @@ -1609,6 +1636,7 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f server->cache_consistency_bitmask[1] &= FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY; server->acl_bitmask = res.acl_bitmask; } + return status; } @@ -1641,7 +1669,7 @@ static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle, .rpc_resp = &res, }; nfs_fattr_init(info->fattr); - return rpc_call_sync(server->client, &msg, 0); + return nfs4_call_sync(server, &msg, &args, &res, 0); } static int nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle, @@ -1731,7 +1759,7 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, }; nfs_fattr_init(fattr); - return rpc_call_sync(server->client, &msg, 0); + return nfs4_call_sync(server, &msg, &args, &res, 0); } static int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr) @@ -1815,7 +1843,7 @@ static int _nfs4_proc_lookupfh(struct nfs_server *server, const struct nfs_fh *d nfs_fattr_init(fattr); dprintk("NFS call lookupfh %s\n", name->name); - status = rpc_call_sync(server->client, &msg, 0); + status = nfs4_call_sync(server, &msg, &args, &res, 0); dprintk("NFS reply lookupfh: %d\n", status); return status; } @@ -1901,7 +1929,7 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry args.access |= NFS4_ACCESS_EXECUTE; } nfs_fattr_init(&fattr); - status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); + status = nfs4_call_sync(server, &msg, &args, &res, 0); if (!status) { entry->mask = 0; if (res.access & NFS4_ACCESS_READ) @@ -1967,7 +1995,7 @@ static int _nfs4_proc_readlink(struct inode *inode, struct page *page, .rpc_resp = &res, }; - return rpc_call_sync(NFS_CLIENT(inode), &msg, 0); + return nfs4_call_sync(NFS_SERVER(inode), &msg, &args, &res, 0); } static int nfs4_proc_readlink(struct inode *inode, struct page *page, @@ -2061,7 +2089,7 @@ static int _nfs4_proc_remove(struct inode *dir, struct qstr *name) int status; nfs_fattr_init(&res.dir_attr); - status = rpc_call_sync(server->client, &msg, 0); + status = nfs4_call_sync(server, &msg, &args, &res, 1); if (status == 0) { update_changeattr(dir, &res.cinfo); nfs_post_op_update_inode(dir, &res.dir_attr); @@ -2129,7 +2157,7 @@ static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name, nfs_fattr_init(res.old_fattr); nfs_fattr_init(res.new_fattr); - status = rpc_call_sync(server->client, &msg, 0); + status = nfs4_call_sync(server, &msg, &arg, &res, 1); if (!status) { update_changeattr(old_dir, &res.old_cinfo); @@ -2178,7 +2206,7 @@ static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr * nfs_fattr_init(res.fattr); nfs_fattr_init(res.dir_attr); - status = rpc_call_sync(server->client, &msg, 0); + status = nfs4_call_sync(server, &msg, &arg, &res, 1); if (!status) { update_changeattr(dir, &res.cinfo); nfs_post_op_update_inode(dir, res.dir_attr); @@ -2239,7 +2267,8 @@ static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir, static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_createdata *data) { - int status = rpc_call_sync(NFS_CLIENT(dir), &data->msg, 0); + int status = nfs4_call_sync(NFS_SERVER(dir), &data->msg, + &data->arg, &data->res, 1); if (status == 0) { update_changeattr(dir, &data->res.dir_cinfo); nfs_post_op_update_inode(dir, data->res.dir_fattr); @@ -2348,7 +2377,7 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, (unsigned long long)cookie); nfs4_setup_readdir(cookie, NFS_COOKIEVERF(dir), dentry, &args); res.pgbase = args.pgbase; - status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); + status = nfs4_call_sync(NFS_SERVER(dir), &msg, &args, &res, 0); if (status == 0) memcpy(NFS_COOKIEVERF(dir), res.verifier.data, NFS4_VERIFIER_SIZE); @@ -2436,7 +2465,7 @@ static int _nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, }; nfs_fattr_init(fsstat->fattr); - return rpc_call_sync(server->client, &msg, 0); + return nfs4_call_sync(server, &msg, &args, &res, 0); } static int nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsstat *fsstat) @@ -2467,7 +2496,7 @@ static int _nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, .rpc_resp = &res, }; - return rpc_call_sync(server->client, &msg, 0); + return nfs4_call_sync(server, &msg, &args, &res, 0); } static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo) @@ -2512,7 +2541,7 @@ static int _nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle } nfs_fattr_init(pathconf->fattr); - return rpc_call_sync(server->client, &msg, 0); + return nfs4_call_sync(server, &msg, &args, &res, 0); } static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, @@ -2781,7 +2810,7 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu resp_buf = buf; buf_to_pages(buf, buflen, args.acl_pages, &args.acl_pgbase); } - ret = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); + ret = nfs4_call_sync(NFS_SERVER(inode), &msg, &args, &res, 0); if (ret) goto out_free; if (res.acl_len > args.acl_len) @@ -2854,7 +2883,7 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl return -EOPNOTSUPP; nfs_inode_return_delegation(inode); buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase); - ret = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); + ret = nfs4_call_sync(server, &msg, &arg, &res, 1); nfs_access_zap_cache(inode); nfs_zap_acl_cache(inode); return ret; @@ -3143,7 +3172,7 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock goto out; lsp = request->fl_u.nfs4_fl.owner; arg.lock_owner.id = lsp->ls_id.id; - status = rpc_call_sync(server->client, &msg, 0); + status = nfs4_call_sync(server, &msg, &arg, &res, 1); switch (status) { case 0: request->fl_type = F_UNLCK; @@ -3736,7 +3765,7 @@ int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, nfs_fattr_init(&fs_locations->fattr); fs_locations->server = server; fs_locations->nlocations = 0; - status = rpc_call_sync(server->client, &msg, 0); + status = nfs4_call_sync(server, &msg, &args, &res, 0); nfs_fixup_referral_attributes(&fs_locations->fattr); dprintk("%s: returned status = %d\n", __func__, status); return status; diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index b47c0fc55d4..206485e5082 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -12,6 +12,9 @@ struct nfs4_session; struct nfs_iostats; struct nlm_host; +struct nfs4_sequence_args; +struct nfs4_sequence_res; +struct nfs_server; /* * The nfs_client identifies our client state to the server. @@ -67,6 +70,11 @@ struct nfs_client { */ char cl_ipaddr[48]; unsigned char cl_id_uniquifier; + int (* cl_call_sync)(struct nfs_server *server, + struct rpc_message *msg, + struct nfs4_sequence_args *args, + struct nfs4_sequence_res *res, + int cache_reply); #endif /* CONFIG_NFS_V4 */ #ifdef CONFIG_NFS_V4_1 -- cgit v1.2.3 From f3752975caa716709c5ea0b0820b86111d921df4 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:22:04 -0400 Subject: nfs41: nfs41: pass *session in seq_args and seq_res To be used for getting the rpc's minorversion and for nfs41 xdr {en,de}coding of the sequence operation. Reset the seq session ptrs for minorversion=0 rpc calls. Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 1 + include/linux/nfs_xdr.h | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 4fc5b385f61..49cf969417c 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -291,6 +291,7 @@ int _nfs4_call_sync(struct nfs_server *server, struct nfs4_sequence_res *res, int cache_reply) { + args->sa_session = res->sr_session = NULL; return rpc_call_sync(server->client, msg, 0); } diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index d837f10c49e..f5675063f95 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -162,11 +162,11 @@ struct nfs4_slot { }; struct nfs4_sequence_args { - /* stub */ + struct nfs4_session *sa_session; }; struct nfs4_sequence_res { - /* stub */ + struct nfs4_session *sr_session; }; /* -- cgit v1.2.3 From 5f7dbd5c752d88310d8fe1feedefd5c6496eff48 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:05 -0400 Subject: nfs41: set up seq_res.sr_slotid Initialize nfs4_sequence_res sr_slotid to NFS4_MAX_SLOT_TABLE. [was nfs41: sequence res use slotid] Signed-off-by: Andy Adamson [pulled definition of struct nfs4_sequence_res.sr_slotid to here] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 5 +++++ fs/nfs/read.c | 1 + fs/nfs/unlink.c | 1 + fs/nfs/write.c | 2 ++ include/linux/nfs_xdr.h | 1 + 5 files changed, 10 insertions(+) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 49cf969417c..5878930b4c3 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -371,6 +371,7 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path, p->o_arg.server = server; p->o_arg.bitmask = server->attr_bitmask; p->o_arg.claim = NFS4_OPEN_CLAIM_NULL; + p->o_res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; if (flags & O_EXCL) { u32 *s = (u32 *) p->o_arg.u.verifier.data; s[0] = jiffies; @@ -1463,6 +1464,7 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait) calldata->res.fattr = &calldata->fattr; calldata->res.seqid = calldata->arg.seqid; calldata->res.server = server; + calldata->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; calldata->path.mnt = mntget(path->mnt); calldata->path.dentry = dget(path->dentry); @@ -3088,6 +3090,7 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co memcpy(&data->stateid, stateid, sizeof(data->stateid)); data->res.fattr = &data->fattr; data->res.server = server; + data->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; nfs_fattr_init(data->res.fattr); data->timestamp = jiffies; data->rpc_status = 0; @@ -3240,6 +3243,7 @@ static struct nfs4_unlockdata *nfs4_alloc_unlockdata(struct file_lock *fl, p->arg.fl = &p->fl; p->arg.seqid = seqid; p->res.seqid = seqid; + p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; p->arg.stateid = &lsp->ls_stateid; p->lsp = lsp; atomic_inc(&lsp->ls_count); @@ -3412,6 +3416,7 @@ static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl, p->arg.lock_owner.clientid = server->nfs_client->cl_clientid; p->arg.lock_owner.id = lsp->ls_id.id; p->res.lock_seqid = p->arg.lock_seqid; + p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; p->lsp = lsp; atomic_inc(&lsp->ls_count); p->ctx = get_nfs_open_context(ctx); diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 4ace3c50a8e..70ba2b4cb9a 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -46,6 +46,7 @@ struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount) memset(p, 0, sizeof(*p)); INIT_LIST_HEAD(&p->pages); p->npages = pagecount; + p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; if (pagecount <= ARRAY_SIZE(p->page_array)) p->pagevec = p->page_array; else { diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index ecc29534777..83d0ce2600a 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c @@ -241,6 +241,7 @@ nfs_async_unlink(struct inode *dir, struct dentry *dentry) status = PTR_ERR(data->cred); goto out_free; } + data->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; status = -EBUSY; spin_lock(&dentry->d_lock); diff --git a/fs/nfs/write.c b/fs/nfs/write.c index e560a78995a..035e6fb9f57 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -52,6 +52,7 @@ struct nfs_write_data *nfs_commitdata_alloc(void) if (p) { memset(p, 0, sizeof(*p)); INIT_LIST_HEAD(&p->pages); + p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; } return p; } @@ -71,6 +72,7 @@ struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount) memset(p, 0, sizeof(*p)); INIT_LIST_HEAD(&p->pages); p->npages = pagecount; + p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; if (pagecount <= ARRAY_SIZE(p->page_array)) p->pagevec = p->page_array; else { diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index f5675063f95..db0d1236aae 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -167,6 +167,7 @@ struct nfs4_sequence_args { struct nfs4_sequence_res { struct nfs4_session *sr_session; + u8 sr_slotid; /* slot used to send request */ }; /* -- cgit v1.2.3 From 6ce183919b4a09289cb0fe4fce960a9faa1e7c6b Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:22:06 -0400 Subject: NFS: use decode_change_info_maxsz for xdr maxsz calculations Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4xdr.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index e448e33b4d0..487a197231f 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -192,12 +192,16 @@ static int nfs4_stat_to_errno(int); decode_verifier_maxsz) #define encode_remove_maxsz (op_encode_hdr_maxsz + \ nfs4_name_maxsz) +#define decode_remove_maxsz (op_decode_hdr_maxsz + \ + decode_change_info_maxsz) #define encode_rename_maxsz (op_encode_hdr_maxsz + \ 2 * nfs4_name_maxsz) -#define decode_rename_maxsz (op_decode_hdr_maxsz + 5 + 5) +#define decode_rename_maxsz (op_decode_hdr_maxsz + \ + decode_change_info_maxsz + \ + decode_change_info_maxsz) #define encode_link_maxsz (op_encode_hdr_maxsz + \ nfs4_name_maxsz) -#define decode_link_maxsz (op_decode_hdr_maxsz + 5) +#define decode_link_maxsz (op_decode_hdr_maxsz + decode_change_info_maxsz) #define encode_lock_maxsz (op_encode_hdr_maxsz + \ 7 + \ 1 + encode_stateid_maxsz + 8) @@ -414,7 +418,7 @@ static int nfs4_stat_to_errno(int); encode_getattr_maxsz) #define NFS4_dec_remove_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - op_decode_hdr_maxsz + 5 + \ + decode_remove_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_rename_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ -- cgit v1.2.3 From 0c4e8c187758258ec58842384fe6a99cf1ce16c7 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:22:07 -0400 Subject: NFS: define and initialize compound_hdr.replen replen holds the running count of expected reply bytes. repl will then be used by encoding routines for xdr_inline_pages offset after which data bytes are to be received directly into the xdr buffer pages. NOTE: According to the nfsv4 and v4.1 RFCs, the replied tag SHOULD be the same is the one sent, but this is not required as a MUST for the server to do so. The server may screw us if it replies a tag of a different length in the compound result. [NFS: cb_compoundhdr.replen is in words not bytes] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4xdr.c | 77 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 487a197231f..c85dbee34b8 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -545,6 +545,7 @@ struct compound_hdr { __be32 * nops_p; uint32_t taglen; char * tag; + uint32_t replen; /* expected reply words */ }; /* @@ -580,9 +581,17 @@ static void encode_string(struct xdr_stream *xdr, unsigned int len, const char * xdr_encode_opaque(p, str, len); } -static void encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr) +static void encode_compound_hdr(struct xdr_stream *xdr, + struct rpc_rqst *req, + struct compound_hdr *hdr) { __be32 *p; + struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; + + /* initialize running count of expected bytes in reply. + * NOTE: the replied tag SHOULD be the same is the one sent, + * but this is not required as a MUST for the server to do so. */ + hdr->replen = RPC_REPHDRSIZE + auth->au_rslack + 3 + hdr->taglen; dprintk("encode_compound: tag=%.*s\n", (int)hdr->taglen, hdr->tag); BUG_ON(hdr->taglen > NFS4_MAXTAGLEN); @@ -1359,7 +1368,7 @@ static int nfs4_xdr_enc_access(struct rpc_rqst *req, __be32 *p, const struct nfs }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_access(&xdr, args->access, &hdr); encode_getfattr(&xdr, args->bitmask, &hdr); @@ -1378,7 +1387,7 @@ static int nfs4_xdr_enc_lookup(struct rpc_rqst *req, __be32 *p, const struct nfs }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->dir_fh, &hdr); encode_lookup(&xdr, args->name, &hdr); encode_getfh(&xdr, &hdr); @@ -1398,7 +1407,7 @@ static int nfs4_xdr_enc_lookup_root(struct rpc_rqst *req, __be32 *p, const struc }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putrootfh(&xdr, &hdr); encode_getfh(&xdr, &hdr); encode_getfattr(&xdr, args->bitmask, &hdr); @@ -1417,7 +1426,7 @@ static int nfs4_xdr_enc_remove(struct rpc_rqst *req, __be32 *p, const struct nfs }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_remove(&xdr, &args->name, &hdr); encode_getfattr(&xdr, args->bitmask, &hdr); @@ -1436,7 +1445,7 @@ static int nfs4_xdr_enc_rename(struct rpc_rqst *req, __be32 *p, const struct nfs }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->old_dir, &hdr); encode_savefh(&xdr, &hdr); encode_putfh(&xdr, args->new_dir, &hdr); @@ -1459,7 +1468,7 @@ static int nfs4_xdr_enc_link(struct rpc_rqst *req, __be32 *p, const struct nfs4_ }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_savefh(&xdr, &hdr); encode_putfh(&xdr, args->dir_fh, &hdr); @@ -1482,7 +1491,7 @@ static int nfs4_xdr_enc_create(struct rpc_rqst *req, __be32 *p, const struct nfs }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->dir_fh, &hdr); encode_savefh(&xdr, &hdr); encode_create(&xdr, args, &hdr); @@ -1513,7 +1522,7 @@ static int nfs4_xdr_enc_getattr(struct rpc_rqst *req, __be32 *p, const struct nf }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_getfattr(&xdr, args->bitmask, &hdr); encode_nops(&hdr); @@ -1531,7 +1540,7 @@ static int nfs4_xdr_enc_close(struct rpc_rqst *req, __be32 *p, struct nfs_closea }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_close(&xdr, args, &hdr); encode_getfattr(&xdr, args->bitmask, &hdr); @@ -1550,7 +1559,7 @@ static int nfs4_xdr_enc_open(struct rpc_rqst *req, __be32 *p, struct nfs_openarg }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_savefh(&xdr, &hdr); encode_open(&xdr, args, &hdr); @@ -1573,7 +1582,7 @@ static int nfs4_xdr_enc_open_confirm(struct rpc_rqst *req, __be32 *p, struct nfs }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_open_confirm(&xdr, args, &hdr); encode_nops(&hdr); @@ -1591,7 +1600,7 @@ static int nfs4_xdr_enc_open_noattr(struct rpc_rqst *req, __be32 *p, struct nfs_ }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_open(&xdr, args, &hdr); encode_getfattr(&xdr, args->bitmask, &hdr); @@ -1610,7 +1619,7 @@ static int nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req, __be32 *p, struct n }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_open_downgrade(&xdr, args, &hdr); encode_getfattr(&xdr, args->bitmask, &hdr); @@ -1629,7 +1638,7 @@ static int nfs4_xdr_enc_lock(struct rpc_rqst *req, __be32 *p, struct nfs_lock_ar }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_lock(&xdr, args, &hdr); encode_nops(&hdr); @@ -1647,7 +1656,7 @@ static int nfs4_xdr_enc_lockt(struct rpc_rqst *req, __be32 *p, struct nfs_lockt_ }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_lockt(&xdr, args, &hdr); encode_nops(&hdr); @@ -1665,7 +1674,7 @@ static int nfs4_xdr_enc_locku(struct rpc_rqst *req, __be32 *p, struct nfs_locku_ }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_locku(&xdr, args, &hdr); encode_nops(&hdr); @@ -1685,7 +1694,7 @@ static int nfs4_xdr_enc_readlink(struct rpc_rqst *req, __be32 *p, const struct n unsigned int replen; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_readlink(&xdr, args, req, &hdr); @@ -1713,7 +1722,7 @@ static int nfs4_xdr_enc_readdir(struct rpc_rqst *req, __be32 *p, const struct nf int replen; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_readdir(&xdr, args, req, &hdr); @@ -1744,7 +1753,7 @@ static int nfs4_xdr_enc_read(struct rpc_rqst *req, __be32 *p, struct nfs_readarg int replen; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_read(&xdr, args, &hdr); @@ -1771,7 +1780,7 @@ static int nfs4_xdr_enc_setattr(struct rpc_rqst *req, __be32 *p, struct nfs_seta }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_setattr(&xdr, args, args->server, &hdr); encode_getfattr(&xdr, args->bitmask, &hdr); @@ -1794,7 +1803,7 @@ nfs4_xdr_enc_getacl(struct rpc_rqst *req, __be32 *p, int replen; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_getattr_two(&xdr, FATTR4_WORD0_ACL, 0, &hdr); @@ -1817,7 +1826,7 @@ static int nfs4_xdr_enc_write(struct rpc_rqst *req, __be32 *p, struct nfs_writea }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_write(&xdr, args, &hdr); req->rq_snd_buf.flags |= XDRBUF_WRITE; @@ -1837,7 +1846,7 @@ static int nfs4_xdr_enc_commit(struct rpc_rqst *req, __be32 *p, struct nfs_write }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_commit(&xdr, args, &hdr); encode_getfattr(&xdr, args->bitmask, &hdr); @@ -1856,7 +1865,7 @@ static int nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, __be32 *p, struct nfs4_fsin }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_fsinfo(&xdr, args->bitmask, &hdr); encode_nops(&hdr); @@ -1874,7 +1883,7 @@ static int nfs4_xdr_enc_pathconf(struct rpc_rqst *req, __be32 *p, const struct n }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_getattr_one(&xdr, args->bitmask[0] & nfs4_pathconf_bitmap[0], &hdr); @@ -1893,7 +1902,7 @@ static int nfs4_xdr_enc_statfs(struct rpc_rqst *req, __be32 *p, const struct nfs }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_getattr_two(&xdr, args->bitmask[0] & nfs4_statfs_bitmap[0], args->bitmask[1] & nfs4_statfs_bitmap[1], &hdr); @@ -1913,7 +1922,7 @@ static int nfs4_xdr_enc_server_caps(struct rpc_rqst *req, __be32 *p, }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fhandle, &hdr); encode_getattr_one(&xdr, FATTR4_WORD0_SUPPORTED_ATTRS| FATTR4_WORD0_LINK_SUPPORT| @@ -1934,7 +1943,7 @@ static int nfs4_xdr_enc_renew(struct rpc_rqst *req, __be32 *p, struct nfs_client }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_renew(&xdr, clp, &hdr); encode_nops(&hdr); return 0; @@ -1951,7 +1960,7 @@ static int nfs4_xdr_enc_setclientid(struct rpc_rqst *req, __be32 *p, struct nfs4 }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_setclientid(&xdr, sc, &hdr); encode_nops(&hdr); return 0; @@ -1969,7 +1978,7 @@ static int nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req, __be32 *p, str const u32 lease_bitmap[2] = { FATTR4_WORD0_LEASE_TIME, 0 }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_setclientid_confirm(&xdr, clp, &hdr); encode_putrootfh(&xdr, &hdr); encode_fsinfo(&xdr, lease_bitmap, &hdr); @@ -1988,7 +1997,7 @@ static int nfs4_xdr_enc_delegreturn(struct rpc_rqst *req, __be32 *p, const struc }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fhandle, &hdr); encode_delegreturn(&xdr, args->stateid, &hdr); encode_getfattr(&xdr, args->bitmask, &hdr); @@ -2009,7 +2018,7 @@ static int nfs4_xdr_enc_fs_locations(struct rpc_rqst *req, __be32 *p, struct nfs int replen; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->dir_fh, &hdr); encode_lookup(&xdr, args->name, &hdr); encode_fs_locations(&xdr, args->bitmask, &hdr); @@ -3989,7 +3998,7 @@ nfs4_xdr_enc_setacl(struct rpc_rqst *req, __be32 *p, struct nfs_setaclargs *args int status; xdr_init_encode(&xdr, &req->rq_snd_buf, p); - encode_compound_hdr(&xdr, &hdr); + encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); status = encode_setacl(&xdr, args, &hdr); encode_nops(&hdr); -- cgit v1.2.3 From dadf0c2767ce7772fc4ff82044f3ba5823e5b79f Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:22:08 -0400 Subject: NFS: update hdr->replen for every encode op Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4xdr.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index c85dbee34b8..746c6a52794 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -749,6 +749,7 @@ static void encode_access(struct xdr_stream *xdr, u32 access, struct compound_hd WRITE32(OP_ACCESS); WRITE32(access); hdr->nops++; + hdr->replen += decode_access_maxsz; } static void encode_close(struct xdr_stream *xdr, const struct nfs_closeargs *arg, struct compound_hdr *hdr) @@ -760,6 +761,7 @@ static void encode_close(struct xdr_stream *xdr, const struct nfs_closeargs *arg WRITE32(arg->seqid->sequence->counter); WRITEMEM(arg->stateid->data, NFS4_STATEID_SIZE); hdr->nops++; + hdr->replen += decode_close_maxsz; } static void encode_commit(struct xdr_stream *xdr, const struct nfs_writeargs *args, struct compound_hdr *hdr) @@ -771,6 +773,7 @@ static void encode_commit(struct xdr_stream *xdr, const struct nfs_writeargs *ar WRITE64(args->offset); WRITE32(args->count); hdr->nops++; + hdr->replen += decode_commit_maxsz; } static void encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *create, struct compound_hdr *hdr) @@ -802,6 +805,7 @@ static void encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg * WRITE32(create->name->len); WRITEMEM(create->name->name, create->name->len); hdr->nops++; + hdr->replen += decode_create_maxsz; encode_attrs(xdr, create->attrs, create->server); } @@ -815,6 +819,7 @@ static void encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap, struct c WRITE32(1); WRITE32(bitmap); hdr->nops++; + hdr->replen += decode_getattr_maxsz; } static void encode_getattr_two(struct xdr_stream *xdr, uint32_t bm0, uint32_t bm1, struct compound_hdr *hdr) @@ -827,6 +832,7 @@ static void encode_getattr_two(struct xdr_stream *xdr, uint32_t bm0, uint32_t bm WRITE32(bm0); WRITE32(bm1); hdr->nops++; + hdr->replen += decode_getattr_maxsz; } static void encode_getfattr(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr) @@ -854,6 +860,7 @@ static void encode_getfh(struct xdr_stream *xdr, struct compound_hdr *hdr) RESERVE_SPACE(4); WRITE32(OP_GETFH); hdr->nops++; + hdr->replen += decode_getfh_maxsz; } static void encode_link(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr) @@ -865,6 +872,7 @@ static void encode_link(struct xdr_stream *xdr, const struct qstr *name, struct WRITE32(name->len); WRITEMEM(name->name, name->len); hdr->nops++; + hdr->replen += decode_link_maxsz; } static inline int nfs4_lock_type(struct file_lock *fl, int block) @@ -912,6 +920,7 @@ static void encode_lock(struct xdr_stream *xdr, const struct nfs_lock_args *args WRITE32(args->lock_seqid->sequence->counter); } hdr->nops++; + hdr->replen += decode_lock_maxsz; } static void encode_lockt(struct xdr_stream *xdr, const struct nfs_lockt_args *args, struct compound_hdr *hdr) @@ -928,6 +937,7 @@ static void encode_lockt(struct xdr_stream *xdr, const struct nfs_lockt_args *ar WRITEMEM("lock id:", 8); WRITE64(args->lock_owner.id); hdr->nops++; + hdr->replen += decode_lockt_maxsz; } static void encode_locku(struct xdr_stream *xdr, const struct nfs_locku_args *args, struct compound_hdr *hdr) @@ -942,6 +952,7 @@ static void encode_locku(struct xdr_stream *xdr, const struct nfs_locku_args *ar WRITE64(args->fl->fl_start); WRITE64(nfs4_lock_length(args->fl)); hdr->nops++; + hdr->replen += decode_locku_maxsz; } static void encode_lookup(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr) @@ -954,6 +965,7 @@ static void encode_lookup(struct xdr_stream *xdr, const struct qstr *name, struc WRITE32(len); WRITEMEM(name->name, len); hdr->nops++; + hdr->replen += decode_lookup_maxsz; } static void encode_share_access(struct xdr_stream *xdr, fmode_t fmode) @@ -1093,6 +1105,7 @@ static void encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg, BUG(); } hdr->nops++; + hdr->replen += decode_open_maxsz; } static void encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_confirmargs *arg, struct compound_hdr *hdr) @@ -1104,6 +1117,7 @@ static void encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_co WRITEMEM(arg->stateid->data, NFS4_STATEID_SIZE); WRITE32(arg->seqid->sequence->counter); hdr->nops++; + hdr->replen += decode_open_confirm_maxsz; } static void encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_closeargs *arg, struct compound_hdr *hdr) @@ -1116,6 +1130,7 @@ static void encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_close WRITE32(arg->seqid->sequence->counter); encode_share_access(xdr, arg->fmode); hdr->nops++; + hdr->replen += decode_open_downgrade_maxsz; } static void @@ -1129,6 +1144,7 @@ encode_putfh(struct xdr_stream *xdr, const struct nfs_fh *fh, struct compound_hd WRITE32(len); WRITEMEM(fh->data, len); hdr->nops++; + hdr->replen += decode_putfh_maxsz; } static void encode_putrootfh(struct xdr_stream *xdr, struct compound_hdr *hdr) @@ -1138,6 +1154,7 @@ static void encode_putrootfh(struct xdr_stream *xdr, struct compound_hdr *hdr) RESERVE_SPACE(4); WRITE32(OP_PUTROOTFH); hdr->nops++; + hdr->replen += decode_putrootfh_maxsz; } static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context *ctx) @@ -1166,6 +1183,7 @@ static void encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args, WRITE64(args->offset); WRITE32(args->count); hdr->nops++; + hdr->replen += decode_read_maxsz; } static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req, struct compound_hdr *hdr) @@ -1191,6 +1209,7 @@ static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg WRITE32(attrs[0] & readdir->bitmask[0]); WRITE32(attrs[1] & readdir->bitmask[1]); hdr->nops++; + hdr->replen += decode_readdir_maxsz; dprintk("%s: cookie = %Lu, verifier = %08x:%08x, bitmap = %08x:%08x\n", __func__, (unsigned long long)readdir->cookie, @@ -1207,6 +1226,7 @@ static void encode_readlink(struct xdr_stream *xdr, const struct nfs4_readlink * RESERVE_SPACE(4); WRITE32(OP_READLINK); hdr->nops++; + hdr->replen += decode_readlink_maxsz; } static void encode_remove(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr) @@ -1218,6 +1238,7 @@ static void encode_remove(struct xdr_stream *xdr, const struct qstr *name, struc WRITE32(name->len); WRITEMEM(name->name, name->len); hdr->nops++; + hdr->replen += decode_remove_maxsz; } static void encode_rename(struct xdr_stream *xdr, const struct qstr *oldname, const struct qstr *newname, struct compound_hdr *hdr) @@ -1233,6 +1254,7 @@ static void encode_rename(struct xdr_stream *xdr, const struct qstr *oldname, co WRITE32(newname->len); WRITEMEM(newname->name, newname->len); hdr->nops++; + hdr->replen += decode_rename_maxsz; } static void encode_renew(struct xdr_stream *xdr, const struct nfs_client *client_stateid, struct compound_hdr *hdr) @@ -1243,6 +1265,7 @@ static void encode_renew(struct xdr_stream *xdr, const struct nfs_client *client WRITE32(OP_RENEW); WRITE64(client_stateid->cl_clientid); hdr->nops++; + hdr->replen += decode_renew_maxsz; } static void @@ -1253,6 +1276,7 @@ encode_restorefh(struct xdr_stream *xdr, struct compound_hdr *hdr) RESERVE_SPACE(4); WRITE32(OP_RESTOREFH); hdr->nops++; + hdr->replen += decode_restorefh_maxsz; } static int @@ -1272,6 +1296,7 @@ encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg, struct compoun WRITE32(arg->acl_len); xdr_write_pages(xdr, arg->acl_pages, arg->acl_pgbase, arg->acl_len); hdr->nops++; + hdr->replen += decode_setacl_maxsz; return 0; } @@ -1283,6 +1308,7 @@ encode_savefh(struct xdr_stream *xdr, struct compound_hdr *hdr) RESERVE_SPACE(4); WRITE32(OP_SAVEFH); hdr->nops++; + hdr->replen += decode_savefh_maxsz; } static void encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs *arg, const struct nfs_server *server, struct compound_hdr *hdr) @@ -1293,6 +1319,7 @@ static void encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs WRITE32(OP_SETATTR); WRITEMEM(arg->stateid.data, NFS4_STATEID_SIZE); hdr->nops++; + hdr->replen += decode_setattr_maxsz; encode_attrs(xdr, arg->iap, server); } @@ -1312,6 +1339,7 @@ static void encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclie RESERVE_SPACE(4); WRITE32(setclientid->sc_cb_ident); hdr->nops++; + hdr->replen += decode_setclientid_maxsz; } static void encode_setclientid_confirm(struct xdr_stream *xdr, const struct nfs_client *client_state, struct compound_hdr *hdr) @@ -1323,6 +1351,7 @@ static void encode_setclientid_confirm(struct xdr_stream *xdr, const struct nfs_ WRITE64(client_state->cl_clientid); WRITEMEM(client_state->cl_confirm.data, NFS4_VERIFIER_SIZE); hdr->nops++; + hdr->replen += decode_setclientid_confirm_maxsz; } static void encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *args, struct compound_hdr *hdr) @@ -1341,6 +1370,7 @@ static void encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *arg xdr_write_pages(xdr, args->pages, args->pgbase, args->count); hdr->nops++; + hdr->replen += decode_write_maxsz; } static void encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *stateid, struct compound_hdr *hdr) @@ -1352,6 +1382,7 @@ static void encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *state WRITE32(OP_DELEGRETURN); WRITEMEM(stateid->data, NFS4_STATEID_SIZE); hdr->nops++; + hdr->replen += decode_delegreturn_maxsz; } /* * END OF "GENERIC" ENCODE ROUTINES. -- cgit v1.2.3 From 28f566942c6b1d929f5e240e69e7081b77b238d3 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:22:09 -0400 Subject: NFS: use dynamically computed compound_hdr.replen for xdr_inline_pages offset As Trond suggested, rather than passing a constant to xdr_inline_pages, keep a running count of the expected reply bytes. In preparation for nfs41, where additional op sequence are expteced when talking to nfs41 servers. [NFS: cb_compoundhdr.replen is in words not bytes] Signed-off-by: Benny Halevy [nfs41: get fs_locations replen before encoding the GETATTR] Signed-off-by: Benny Halevy [nfs41: get getacl replen before encoding the GETATTR] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4xdr.c | 48 ++++++++++-------------------------------------- 1 file changed, 10 insertions(+), 38 deletions(-) diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 746c6a52794..68889469d11 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -1721,20 +1721,13 @@ static int nfs4_xdr_enc_readlink(struct rpc_rqst *req, __be32 *p, const struct n struct compound_hdr hdr = { .nops = 0, }; - struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; - unsigned int replen; xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_readlink(&xdr, args, req, &hdr); - /* set up reply kvec - * toplevel_status + taglen + rescount + OP_PUTFH + status - * + OP_READLINK + status + string length = 8 - */ - replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS4_dec_readlink_sz) << 2; - xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, + xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2, args->pages, args->pgbase, args->pglen); encode_nops(&hdr); return 0; @@ -1749,23 +1742,16 @@ static int nfs4_xdr_enc_readdir(struct rpc_rqst *req, __be32 *p, const struct nf struct compound_hdr hdr = { .nops = 0, }; - struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; - int replen; xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_readdir(&xdr, args, req, &hdr); - /* set up reply kvec - * toplevel_status + taglen + rescount + OP_PUTFH + status - * + OP_READDIR + status + verifer(2) = 9 - */ - replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS4_dec_readdir_sz) << 2; - xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, + xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2, args->pages, args->pgbase, args->count); dprintk("%s: inlined page args = (%u, %p, %u, %u)\n", - __func__, replen, args->pages, + __func__, hdr.replen << 2, args->pages, args->pgbase, args->count); encode_nops(&hdr); return 0; @@ -1776,24 +1762,17 @@ static int nfs4_xdr_enc_readdir(struct rpc_rqst *req, __be32 *p, const struct nf */ static int nfs4_xdr_enc_read(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args) { - struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; struct xdr_stream xdr; struct compound_hdr hdr = { .nops = 0, }; - int replen; xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_read(&xdr, args, &hdr); - /* set up reply kvec - * toplevel status + taglen=0 + rescount + OP_PUTFH + status - * + OP_READ + status + eof + datalen = 9 - */ - replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS4_dec_read_sz) << 2; - xdr_inline_pages(&req->rq_rcv_buf, replen, + xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2, args->pages, args->pgbase, args->count); req->rq_rcv_buf.flags |= XDRBUF_READ; encode_nops(&hdr); @@ -1827,20 +1806,18 @@ nfs4_xdr_enc_getacl(struct rpc_rqst *req, __be32 *p, struct nfs_getaclargs *args) { struct xdr_stream xdr; - struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; struct compound_hdr hdr = { .nops = 0, }; - int replen; + uint32_t replen; xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->fh, &hdr); + replen = hdr.replen + nfs4_fattr_bitmap_maxsz + 1; encode_getattr_two(&xdr, FATTR4_WORD0_ACL, 0, &hdr); - /* set up reply buffer: */ - replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS4_dec_getacl_sz) << 2; - xdr_inline_pages(&req->rq_rcv_buf, replen, + xdr_inline_pages(&req->rq_rcv_buf, replen << 2, args->acl_pages, args->acl_pgbase, args->acl_len); encode_nops(&hdr); return 0; @@ -2045,21 +2022,16 @@ static int nfs4_xdr_enc_fs_locations(struct rpc_rqst *req, __be32 *p, struct nfs struct compound_hdr hdr = { .nops = 0, }; - struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; - int replen; + uint32_t replen; xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); encode_putfh(&xdr, args->dir_fh, &hdr); encode_lookup(&xdr, args->name, &hdr); + replen = hdr.replen; /* get the attribute into args->page */ encode_fs_locations(&xdr, args->bitmask, &hdr); - /* set up reply - * toplevel_status + OP_PUTFH + status - * + OP_LOOKUP + status + OP_GETATTR + status = 7 - */ - replen = (RPC_REPHDRSIZE + auth->au_rslack + 7) << 2; - xdr_inline_pages(&req->rq_rcv_buf, replen, &args->page, + xdr_inline_pages(&req->rq_rcv_buf, replen << 2, &args->page, 0, PAGE_SIZE); encode_nops(&hdr); return 0; -- cgit v1.2.3 From 66cc042970f7077c66be65a780eb3a60a9bcbf0b Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:22:10 -0400 Subject: nfs41: encode minorversion in compound header Signed-off-by: Andy Adamdon Signed-off-by: Benny Halevy [nfs41: pass *session in seq_args and seq_res] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4xdr.c | 70 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 68889469d11..19ca0b519a1 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -546,6 +546,7 @@ struct compound_hdr { uint32_t taglen; char * tag; uint32_t replen; /* expected reply words */ + u32 minorversion; }; /* @@ -598,7 +599,7 @@ static void encode_compound_hdr(struct xdr_stream *xdr, RESERVE_SPACE(12+(XDR_QUADLEN(hdr->taglen)<<2)); WRITE32(hdr->taglen); WRITEMEM(hdr->tag, hdr->taglen); - WRITE32(NFS4_MINOR_VERSION); + WRITE32(hdr->minorversion); hdr->nops_p = p; WRITE32(hdr->nops); } @@ -1388,6 +1389,15 @@ static void encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *state * END OF "GENERIC" ENCODE ROUTINES. */ +static u32 nfs4_xdr_minorversion(const struct nfs4_sequence_args *args) +{ +#if defined(CONFIG_NFS_V4_1) + if (args->sa_session) + return args->sa_session->clp->cl_minorversion; +#endif /* CONFIG_NFS_V4_1 */ + return 0; +} + /* * Encode an ACCESS request */ @@ -1395,7 +1405,7 @@ static int nfs4_xdr_enc_access(struct rpc_rqst *req, __be32 *p, const struct nfs { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1414,7 +1424,7 @@ static int nfs4_xdr_enc_lookup(struct rpc_rqst *req, __be32 *p, const struct nfs { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1434,7 +1444,7 @@ static int nfs4_xdr_enc_lookup_root(struct rpc_rqst *req, __be32 *p, const struc { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1453,7 +1463,7 @@ static int nfs4_xdr_enc_remove(struct rpc_rqst *req, __be32 *p, const struct nfs { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1472,7 +1482,7 @@ static int nfs4_xdr_enc_rename(struct rpc_rqst *req, __be32 *p, const struct nfs { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1495,7 +1505,7 @@ static int nfs4_xdr_enc_link(struct rpc_rqst *req, __be32 *p, const struct nfs4_ { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1518,7 +1528,7 @@ static int nfs4_xdr_enc_create(struct rpc_rqst *req, __be32 *p, const struct nfs { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1549,7 +1559,7 @@ static int nfs4_xdr_enc_getattr(struct rpc_rqst *req, __be32 *p, const struct nf { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1567,7 +1577,7 @@ static int nfs4_xdr_enc_close(struct rpc_rqst *req, __be32 *p, struct nfs_closea { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1586,7 +1596,7 @@ static int nfs4_xdr_enc_open(struct rpc_rqst *req, __be32 *p, struct nfs_openarg { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1627,7 +1637,7 @@ static int nfs4_xdr_enc_open_noattr(struct rpc_rqst *req, __be32 *p, struct nfs_ { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1646,7 +1656,7 @@ static int nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req, __be32 *p, struct n { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1665,7 +1675,7 @@ static int nfs4_xdr_enc_lock(struct rpc_rqst *req, __be32 *p, struct nfs_lock_ar { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1683,7 +1693,7 @@ static int nfs4_xdr_enc_lockt(struct rpc_rqst *req, __be32 *p, struct nfs_lockt_ { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1701,7 +1711,7 @@ static int nfs4_xdr_enc_locku(struct rpc_rqst *req, __be32 *p, struct nfs_locku_ { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1719,7 +1729,7 @@ static int nfs4_xdr_enc_readlink(struct rpc_rqst *req, __be32 *p, const struct n { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1740,7 +1750,7 @@ static int nfs4_xdr_enc_readdir(struct rpc_rqst *req, __be32 *p, const struct nf { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1764,7 +1774,7 @@ static int nfs4_xdr_enc_read(struct rpc_rqst *req, __be32 *p, struct nfs_readarg { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1786,7 +1796,7 @@ static int nfs4_xdr_enc_setattr(struct rpc_rqst *req, __be32 *p, struct nfs_seta { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1807,7 +1817,7 @@ nfs4_xdr_enc_getacl(struct rpc_rqst *req, __be32 *p, { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; uint32_t replen; @@ -1830,7 +1840,7 @@ static int nfs4_xdr_enc_write(struct rpc_rqst *req, __be32 *p, struct nfs_writea { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1850,7 +1860,7 @@ static int nfs4_xdr_enc_commit(struct rpc_rqst *req, __be32 *p, struct nfs_write { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1869,7 +1879,7 @@ static int nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, __be32 *p, struct nfs4_fsin { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1887,7 +1897,7 @@ static int nfs4_xdr_enc_pathconf(struct rpc_rqst *req, __be32 *p, const struct n { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1906,7 +1916,7 @@ static int nfs4_xdr_enc_statfs(struct rpc_rqst *req, __be32 *p, const struct nfs { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1926,7 +1936,7 @@ static int nfs4_xdr_enc_server_caps(struct rpc_rqst *req, __be32 *p, { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -2001,7 +2011,7 @@ static int nfs4_xdr_enc_delegreturn(struct rpc_rqst *req, __be32 *p, const struc { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -2020,7 +2030,7 @@ static int nfs4_xdr_enc_fs_locations(struct rpc_rqst *req, __be32 *p, struct nfs { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; uint32_t replen; @@ -3996,7 +4006,7 @@ nfs4_xdr_enc_setacl(struct rpc_rqst *req, __be32 *p, struct nfs_setaclargs *args { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 0, + .minorversion = nfs4_xdr_minorversion(&args->seq_args), }; int status; -- cgit v1.2.3 From 9b7b9fcc9c124b8a2a079f748239ce9b7a8d8304 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:11 -0400 Subject: nfs41: xdr {encode,decode}_sequence Implement stubs for encode and decode sequence, defined as no-ops when CONFIG_NFS_V4_1 is not defined. Add the nfsv41 encode and decode sizes. Add encode_sequence to all nfs4_enc_* routines and decode_sequence to all nfs4_dec_* routines as required by v41. [was nfs41: minorversion support for xdr] [added nfs_client argument to encode_sequence so not to use sequence_args to pass sa_session] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: pass *session in seq_args and seq_res] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4xdr.c | 242 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 232 insertions(+), 10 deletions(-) diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 19ca0b519a1..5b944cd5721 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -244,43 +244,63 @@ static int nfs4_stat_to_errno(int); (encode_getattr_maxsz) #define decode_fs_locations_maxsz \ (0) + +#if defined(CONFIG_NFS_V4_1) +#define encode_sequence_maxsz 0 /* stub */ +#define decode_sequence_maxsz 0 /* stub */ +#else /* CONFIG_NFS_V4_1 */ +#define encode_sequence_maxsz 0 +#define decode_sequence_maxsz 0 +#endif /* CONFIG_NFS_V4_1 */ + #define NFS4_enc_compound_sz (1024) /* XXX: large enough? */ #define NFS4_dec_compound_sz (1024) /* XXX: large enough? */ #define NFS4_enc_read_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_read_maxsz) #define NFS4_dec_read_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_read_maxsz) #define NFS4_enc_readlink_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_readlink_maxsz) #define NFS4_dec_readlink_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_readlink_maxsz) #define NFS4_enc_readdir_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_readdir_maxsz) #define NFS4_dec_readdir_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_readdir_maxsz) #define NFS4_enc_write_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_write_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_write_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_write_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_commit_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_commit_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_commit_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_commit_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_open_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_savefh_maxsz + \ encode_open_maxsz + \ @@ -289,6 +309,7 @@ static int nfs4_stat_to_errno(int); encode_restorefh_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_open_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_savefh_maxsz + \ decode_open_maxsz + \ @@ -305,43 +326,53 @@ static int nfs4_stat_to_errno(int); decode_putfh_maxsz + \ decode_open_confirm_maxsz) #define NFS4_enc_open_noattr_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_open_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_open_noattr_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_open_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_open_downgrade_sz \ (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_open_downgrade_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_open_downgrade_sz \ (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_open_downgrade_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_close_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_close_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_close_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_close_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_setattr_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_setattr_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_setattr_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_setattr_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_fsinfo_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_fsinfo_maxsz) #define NFS4_dec_fsinfo_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_fsinfo_maxsz) #define NFS4_enc_renew_sz (compound_encode_hdr_maxsz + \ @@ -363,64 +394,81 @@ static int nfs4_stat_to_errno(int); decode_putrootfh_maxsz + \ decode_fsinfo_maxsz) #define NFS4_enc_lock_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_lock_maxsz) #define NFS4_dec_lock_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_lock_maxsz) #define NFS4_enc_lockt_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_lockt_maxsz) #define NFS4_dec_lockt_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_lockt_maxsz) #define NFS4_enc_locku_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_locku_maxsz) #define NFS4_dec_locku_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_locku_maxsz) #define NFS4_enc_access_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_access_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_access_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_access_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_getattr_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_getattr_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_lookup_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_lookup_maxsz + \ encode_getattr_maxsz + \ encode_getfh_maxsz) #define NFS4_dec_lookup_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_lookup_maxsz + \ decode_getattr_maxsz + \ decode_getfh_maxsz) #define NFS4_enc_lookup_root_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putrootfh_maxsz + \ encode_getattr_maxsz + \ encode_getfh_maxsz) #define NFS4_dec_lookup_root_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putrootfh_maxsz + \ decode_getattr_maxsz + \ decode_getfh_maxsz) #define NFS4_enc_remove_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_remove_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_remove_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_remove_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_rename_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_savefh_maxsz + \ encode_putfh_maxsz + \ @@ -429,6 +477,7 @@ static int nfs4_stat_to_errno(int); encode_restorefh_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_rename_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_savefh_maxsz + \ decode_putfh_maxsz + \ @@ -437,6 +486,7 @@ static int nfs4_stat_to_errno(int); decode_restorefh_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_link_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_savefh_maxsz + \ encode_putfh_maxsz + \ @@ -445,6 +495,7 @@ static int nfs4_stat_to_errno(int); encode_restorefh_maxsz + \ decode_getattr_maxsz) #define NFS4_dec_link_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_savefh_maxsz + \ decode_putfh_maxsz + \ @@ -453,16 +504,19 @@ static int nfs4_stat_to_errno(int); decode_restorefh_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_symlink_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_symlink_maxsz + \ encode_getattr_maxsz + \ encode_getfh_maxsz) #define NFS4_dec_symlink_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_symlink_maxsz + \ decode_getattr_maxsz + \ decode_getfh_maxsz) #define NFS4_enc_create_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_savefh_maxsz + \ encode_create_maxsz + \ @@ -471,6 +525,7 @@ static int nfs4_stat_to_errno(int); encode_restorefh_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_create_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_savefh_maxsz + \ decode_create_maxsz + \ @@ -479,49 +534,63 @@ static int nfs4_stat_to_errno(int); decode_restorefh_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_pathconf_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_pathconf_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_statfs_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_statfs_maxsz) #define NFS4_dec_statfs_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_statfs_maxsz) #define NFS4_enc_server_caps_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_server_caps_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_delegreturn_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_delegreturn_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_delegreturn_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_delegreturn_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_getacl_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_getacl_maxsz) #define NFS4_dec_getacl_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_getacl_maxsz) #define NFS4_enc_setacl_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_setacl_maxsz) #define NFS4_dec_setacl_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_setacl_maxsz) #define NFS4_enc_fs_locations_sz \ (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_lookup_maxsz + \ encode_fs_locations_maxsz) #define NFS4_dec_fs_locations_sz \ (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_lookup_maxsz + \ decode_fs_locations_maxsz) @@ -1385,6 +1454,24 @@ static void encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *state hdr->nops++; hdr->replen += decode_delegreturn_maxsz; } + +/* NFSv4.1 operations */ +static void encode_sequence(struct xdr_stream *xdr, + const struct nfs4_sequence_args *args, + struct compound_hdr *hdr) +{ +#if defined(CONFIG_NFS_V4_1) + struct nfs4_session *session = args->sa_session; + + if (!session) + return; + + /* stub */ + hdr->nops++; + hdr->replen += decode_sequence_maxsz; +#endif /* CONFIG_NFS_V4_1 */ +} + /* * END OF "GENERIC" ENCODE ROUTINES. */ @@ -1410,6 +1497,7 @@ static int nfs4_xdr_enc_access(struct rpc_rqst *req, __be32 *p, const struct nfs xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_access(&xdr, args->access, &hdr); encode_getfattr(&xdr, args->bitmask, &hdr); @@ -1429,6 +1517,7 @@ static int nfs4_xdr_enc_lookup(struct rpc_rqst *req, __be32 *p, const struct nfs xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->dir_fh, &hdr); encode_lookup(&xdr, args->name, &hdr); encode_getfh(&xdr, &hdr); @@ -1449,6 +1538,7 @@ static int nfs4_xdr_enc_lookup_root(struct rpc_rqst *req, __be32 *p, const struc xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putrootfh(&xdr, &hdr); encode_getfh(&xdr, &hdr); encode_getfattr(&xdr, args->bitmask, &hdr); @@ -1468,6 +1558,7 @@ static int nfs4_xdr_enc_remove(struct rpc_rqst *req, __be32 *p, const struct nfs xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_remove(&xdr, &args->name, &hdr); encode_getfattr(&xdr, args->bitmask, &hdr); @@ -1487,6 +1578,7 @@ static int nfs4_xdr_enc_rename(struct rpc_rqst *req, __be32 *p, const struct nfs xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->old_dir, &hdr); encode_savefh(&xdr, &hdr); encode_putfh(&xdr, args->new_dir, &hdr); @@ -1510,6 +1602,7 @@ static int nfs4_xdr_enc_link(struct rpc_rqst *req, __be32 *p, const struct nfs4_ xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_savefh(&xdr, &hdr); encode_putfh(&xdr, args->dir_fh, &hdr); @@ -1533,6 +1626,7 @@ static int nfs4_xdr_enc_create(struct rpc_rqst *req, __be32 *p, const struct nfs xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->dir_fh, &hdr); encode_savefh(&xdr, &hdr); encode_create(&xdr, args, &hdr); @@ -1564,6 +1658,7 @@ static int nfs4_xdr_enc_getattr(struct rpc_rqst *req, __be32 *p, const struct nf xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_getfattr(&xdr, args->bitmask, &hdr); encode_nops(&hdr); @@ -1582,6 +1677,7 @@ static int nfs4_xdr_enc_close(struct rpc_rqst *req, __be32 *p, struct nfs_closea xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_close(&xdr, args, &hdr); encode_getfattr(&xdr, args->bitmask, &hdr); @@ -1601,6 +1697,7 @@ static int nfs4_xdr_enc_open(struct rpc_rqst *req, __be32 *p, struct nfs_openarg xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_savefh(&xdr, &hdr); encode_open(&xdr, args, &hdr); @@ -1642,6 +1739,7 @@ static int nfs4_xdr_enc_open_noattr(struct rpc_rqst *req, __be32 *p, struct nfs_ xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_open(&xdr, args, &hdr); encode_getfattr(&xdr, args->bitmask, &hdr); @@ -1661,6 +1759,7 @@ static int nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req, __be32 *p, struct n xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_open_downgrade(&xdr, args, &hdr); encode_getfattr(&xdr, args->bitmask, &hdr); @@ -1680,6 +1779,7 @@ static int nfs4_xdr_enc_lock(struct rpc_rqst *req, __be32 *p, struct nfs_lock_ar xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_lock(&xdr, args, &hdr); encode_nops(&hdr); @@ -1698,6 +1798,7 @@ static int nfs4_xdr_enc_lockt(struct rpc_rqst *req, __be32 *p, struct nfs_lockt_ xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_lockt(&xdr, args, &hdr); encode_nops(&hdr); @@ -1716,6 +1817,7 @@ static int nfs4_xdr_enc_locku(struct rpc_rqst *req, __be32 *p, struct nfs_locku_ xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_locku(&xdr, args, &hdr); encode_nops(&hdr); @@ -1734,6 +1836,7 @@ static int nfs4_xdr_enc_readlink(struct rpc_rqst *req, __be32 *p, const struct n xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_readlink(&xdr, args, req, &hdr); @@ -1755,6 +1858,7 @@ static int nfs4_xdr_enc_readdir(struct rpc_rqst *req, __be32 *p, const struct nf xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_readdir(&xdr, args, req, &hdr); @@ -1779,6 +1883,7 @@ static int nfs4_xdr_enc_read(struct rpc_rqst *req, __be32 *p, struct nfs_readarg xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_read(&xdr, args, &hdr); @@ -1801,6 +1906,7 @@ static int nfs4_xdr_enc_setattr(struct rpc_rqst *req, __be32 *p, struct nfs_seta xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_setattr(&xdr, args, args->server, &hdr); encode_getfattr(&xdr, args->bitmask, &hdr); @@ -1823,6 +1929,7 @@ nfs4_xdr_enc_getacl(struct rpc_rqst *req, __be32 *p, xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fh, &hdr); replen = hdr.replen + nfs4_fattr_bitmap_maxsz + 1; encode_getattr_two(&xdr, FATTR4_WORD0_ACL, 0, &hdr); @@ -1845,6 +1952,7 @@ static int nfs4_xdr_enc_write(struct rpc_rqst *req, __be32 *p, struct nfs_writea xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_write(&xdr, args, &hdr); req->rq_snd_buf.flags |= XDRBUF_WRITE; @@ -1865,6 +1973,7 @@ static int nfs4_xdr_enc_commit(struct rpc_rqst *req, __be32 *p, struct nfs_write xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_commit(&xdr, args, &hdr); encode_getfattr(&xdr, args->bitmask, &hdr); @@ -1884,6 +1993,7 @@ static int nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, __be32 *p, struct nfs4_fsin xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_fsinfo(&xdr, args->bitmask, &hdr); encode_nops(&hdr); @@ -1902,6 +2012,7 @@ static int nfs4_xdr_enc_pathconf(struct rpc_rqst *req, __be32 *p, const struct n xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_getattr_one(&xdr, args->bitmask[0] & nfs4_pathconf_bitmap[0], &hdr); @@ -1921,6 +2032,7 @@ static int nfs4_xdr_enc_statfs(struct rpc_rqst *req, __be32 *p, const struct nfs xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fh, &hdr); encode_getattr_two(&xdr, args->bitmask[0] & nfs4_statfs_bitmap[0], args->bitmask[1] & nfs4_statfs_bitmap[1], &hdr); @@ -1941,6 +2053,7 @@ static int nfs4_xdr_enc_server_caps(struct rpc_rqst *req, __be32 *p, xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fhandle, &hdr); encode_getattr_one(&xdr, FATTR4_WORD0_SUPPORTED_ATTRS| FATTR4_WORD0_LINK_SUPPORT| @@ -2016,6 +2129,7 @@ static int nfs4_xdr_enc_delegreturn(struct rpc_rqst *req, __be32 *p, const struc xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fhandle, &hdr); encode_delegreturn(&xdr, args->stateid, &hdr); encode_getfattr(&xdr, args->bitmask, &hdr); @@ -2036,6 +2150,7 @@ static int nfs4_xdr_enc_fs_locations(struct rpc_rqst *req, __be32 *p, struct nfs xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->dir_fh, &hdr); encode_lookup(&xdr, args->name, &hdr); replen = hdr.replen; /* get the attribute into args->page */ @@ -3762,6 +3877,20 @@ static int decode_delegreturn(struct xdr_stream *xdr) return decode_op_hdr(xdr, OP_DELEGRETURN); } +static int decode_sequence(struct xdr_stream *xdr, + struct nfs4_sequence_res *res, + struct rpc_rqst *rqstp) +{ +#if defined(CONFIG_NFS_V4_1) + if (!res->sr_session) + return 0; + + /* stub */ +#endif /* CONFIG_NFS_V4_1 */ + + return 0; +} + /* * END OF "GENERIC" DECODE ROUTINES. */ @@ -3777,6 +3906,9 @@ static int nfs4_xdr_dec_open_downgrade(struct rpc_rqst *rqstp, __be32 *p, struct xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); if (status) goto out; status = decode_putfh(&xdr); @@ -3800,7 +3932,11 @@ static int nfs4_xdr_dec_access(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_ac int status; xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); - if ((status = decode_compound_hdr(&xdr, &hdr)) != 0) + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); + if (status) goto out; status = decode_putfh(&xdr); if (status != 0) @@ -3823,7 +3959,11 @@ static int nfs4_xdr_dec_lookup(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_lo int status; xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); - if ((status = decode_compound_hdr(&xdr, &hdr)) != 0) + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); + if (status) goto out; if ((status = decode_putfh(&xdr)) != 0) goto out; @@ -3846,7 +3986,11 @@ static int nfs4_xdr_dec_lookup_root(struct rpc_rqst *rqstp, __be32 *p, struct nf int status; xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); - if ((status = decode_compound_hdr(&xdr, &hdr)) != 0) + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); + if (status) goto out; if ((status = decode_putrootfh(&xdr)) != 0) goto out; @@ -3866,7 +4010,11 @@ static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, __be32 *p, struct nfs_rem int status; xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); - if ((status = decode_compound_hdr(&xdr, &hdr)) != 0) + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); + if (status) goto out; if ((status = decode_putfh(&xdr)) != 0) goto out; @@ -3887,7 +4035,11 @@ static int nfs4_xdr_dec_rename(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_re int status; xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); - if ((status = decode_compound_hdr(&xdr, &hdr)) != 0) + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); + if (status) goto out; if ((status = decode_putfh(&xdr)) != 0) goto out; @@ -3917,7 +4069,11 @@ static int nfs4_xdr_dec_link(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_link int status; xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); - if ((status = decode_compound_hdr(&xdr, &hdr)) != 0) + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); + if (status) goto out; if ((status = decode_putfh(&xdr)) != 0) goto out; @@ -3950,7 +4106,11 @@ static int nfs4_xdr_dec_create(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_cr int status; xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); - if ((status = decode_compound_hdr(&xdr, &hdr)) != 0) + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); + if (status) goto out; if ((status = decode_putfh(&xdr)) != 0) goto out; @@ -3988,6 +4148,9 @@ static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_g xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); if (status) goto out; status = decode_putfh(&xdr); @@ -4012,6 +4175,7 @@ nfs4_xdr_enc_setacl(struct rpc_rqst *req, __be32 *p, struct nfs_setaclargs *args xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->seq_args, &hdr); encode_putfh(&xdr, args->fh, &hdr); status = encode_setacl(&xdr, args, &hdr); encode_nops(&hdr); @@ -4031,6 +4195,9 @@ nfs4_xdr_dec_setacl(struct rpc_rqst *rqstp, __be32 *p, xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); if (status) goto out; status = decode_putfh(&xdr); @@ -4054,6 +4221,9 @@ nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, __be32 *p, xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); if (status) goto out; status = decode_putfh(&xdr); @@ -4076,6 +4246,9 @@ static int nfs4_xdr_dec_close(struct rpc_rqst *rqstp, __be32 *p, struct nfs_clos xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); if (status) goto out; status = decode_putfh(&xdr); @@ -4106,6 +4279,9 @@ static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, __be32 *p, struct nfs_openr xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); if (status) goto out; status = decode_putfh(&xdr); @@ -4160,6 +4336,9 @@ static int nfs4_xdr_dec_open_noattr(struct rpc_rqst *rqstp, __be32 *p, struct nf xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); if (status) goto out; status = decode_putfh(&xdr); @@ -4184,6 +4363,9 @@ static int nfs4_xdr_dec_setattr(struct rpc_rqst *rqstp, __be32 *p, struct nfs_se xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); if (status) goto out; status = decode_putfh(&xdr); @@ -4208,6 +4390,9 @@ static int nfs4_xdr_dec_lock(struct rpc_rqst *rqstp, __be32 *p, struct nfs_lock_ xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); if (status) goto out; status = decode_putfh(&xdr); @@ -4229,6 +4414,9 @@ static int nfs4_xdr_dec_lockt(struct rpc_rqst *rqstp, __be32 *p, struct nfs_lock xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); if (status) goto out; status = decode_putfh(&xdr); @@ -4250,6 +4438,9 @@ static int nfs4_xdr_dec_locku(struct rpc_rqst *rqstp, __be32 *p, struct nfs_lock xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); if (status) goto out; status = decode_putfh(&xdr); @@ -4272,6 +4463,9 @@ static int nfs4_xdr_dec_readlink(struct rpc_rqst *rqstp, __be32 *p, xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); if (status) goto out; status = decode_putfh(&xdr); @@ -4293,6 +4487,9 @@ static int nfs4_xdr_dec_readdir(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_r xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); if (status) goto out; status = decode_putfh(&xdr); @@ -4314,6 +4511,9 @@ static int nfs4_xdr_dec_read(struct rpc_rqst *rqstp, __be32 *p, struct nfs_readr xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); if (status) goto out; status = decode_putfh(&xdr); @@ -4337,6 +4537,9 @@ static int nfs4_xdr_dec_write(struct rpc_rqst *rqstp, __be32 *p, struct nfs_writ xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); if (status) goto out; status = decode_putfh(&xdr); @@ -4363,6 +4566,9 @@ static int nfs4_xdr_dec_commit(struct rpc_rqst *rqstp, __be32 *p, struct nfs_wri xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); if (status) goto out; status = decode_putfh(&xdr); @@ -4388,6 +4594,8 @@ static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, __be32 *p, xdr_init_decode(&xdr, &req->rq_rcv_buf, p); status = decode_compound_hdr(&xdr, &hdr); + if (!status) + status = decode_sequence(&xdr, &res->seq_res, req); if (!status) status = decode_putfh(&xdr); if (!status) @@ -4407,6 +4615,8 @@ static int nfs4_xdr_dec_pathconf(struct rpc_rqst *req, __be32 *p, xdr_init_decode(&xdr, &req->rq_rcv_buf, p); status = decode_compound_hdr(&xdr, &hdr); + if (!status) + status = decode_sequence(&xdr, &res->seq_res, req); if (!status) status = decode_putfh(&xdr); if (!status) @@ -4426,6 +4636,8 @@ static int nfs4_xdr_dec_statfs(struct rpc_rqst *req, __be32 *p, xdr_init_decode(&xdr, &req->rq_rcv_buf, p); status = decode_compound_hdr(&xdr, &hdr); + if (!status) + status = decode_sequence(&xdr, &res->seq_res, req); if (!status) status = decode_putfh(&xdr); if (!status) @@ -4443,7 +4655,11 @@ static int nfs4_xdr_dec_server_caps(struct rpc_rqst *req, __be32 *p, struct nfs4 int status; xdr_init_decode(&xdr, &req->rq_rcv_buf, p); - if ((status = decode_compound_hdr(&xdr, &hdr)) != 0) + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, req); + if (status) goto out; if ((status = decode_putfh(&xdr)) != 0) goto out; @@ -4516,7 +4732,10 @@ static int nfs4_xdr_dec_delegreturn(struct rpc_rqst *rqstp, __be32 *p, struct nf xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); status = decode_compound_hdr(&xdr, &hdr); - if (status != 0) + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, rqstp); + if (status) goto out; status = decode_putfh(&xdr); if (status != 0) @@ -4539,7 +4758,10 @@ static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req, __be32 *p, xdr_init_decode(&xdr, &req->rq_rcv_buf, p); status = decode_compound_hdr(&xdr, &hdr); - if (status != 0) + if (status) + goto out; + status = decode_sequence(&xdr, &res->seq_res, req); + if (status) goto out; if ((status = decode_putfh(&xdr)) != 0) goto out; -- cgit v1.2.3 From ce5039c1be1fd04c2d9b1af80d41a6d003f2e20c Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:13 -0400 Subject: nfs41: nfs4_setup_sequence Perform the nfs4_setup_sequence in the rpc_call_prepare state. If a session slot is not available, we will rpc_sleep_on the slot wait queue leaving the tk_action as rpc_call_prepare. Once we have a session slot, hang on to it even through rpc_restart_calls. Ensure the nfs41_sequence_res sr_slot pointer is NULL before rpc_run_task is called as nfs41_setup_sequence will only find a new slot if it is NULL. A future patch will call free slot after any rpc_restart_calls, and handle the rpc restart that result from a sequence operation error. Signed-off-by: Rahul Iyer [nfs41: sequence res use slotid] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: simplify nfs4_call_sync] Signed-off-by: Benny Halevy [nfs41: nfs4_call_sync] [nfs41: check for session not minorversion] [nfs41: remove rpc_message from nfs41_call_sync_args] [moved NFS4_MAX_SLOT_TABLE logic into nfs41_setup_sequence] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: nfs41_call_sync_data use nfs_client not nfs_server] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: expose nfs4_call_sync_session for lease renewal] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: remove unnecessary return check] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 2 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 5878930b4c3..90aed76e593 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -273,14 +273,110 @@ static void renew_lease(const struct nfs_server *server, unsigned long timestamp #if defined(CONFIG_NFS_V4_1) +static int nfs41_setup_sequence(struct nfs4_session *session, + struct nfs4_sequence_args *args, + struct nfs4_sequence_res *res, + int cache_reply, + struct rpc_task *task) +{ + /* slot already allocated? */ + if (res->sr_slotid != NFS4_MAX_SLOT_TABLE) + return 0; + + memset(res, 0, sizeof(*res)); + res->sr_slotid = NFS4_MAX_SLOT_TABLE; + return 0; +} + +int nfs4_setup_sequence(struct nfs_client *clp, + struct nfs4_sequence_args *args, + struct nfs4_sequence_res *res, + int cache_reply, + struct rpc_task *task) +{ + int ret = 0; + + dprintk("--> %s clp %p session %p sr_slotid %d\n", + __func__, clp, clp->cl_session, res->sr_slotid); + + if (!nfs4_has_session(clp)) + goto out; + ret = nfs41_setup_sequence(clp->cl_session, args, res, cache_reply, + task); + if (ret != -EAGAIN) { + /* terminate rpc task */ + task->tk_status = ret; + task->tk_action = NULL; + } +out: + dprintk("<-- %s status=%d\n", __func__, ret); + return ret; +} + +struct nfs41_call_sync_data { + struct nfs_client *clp; + struct nfs4_sequence_args *seq_args; + struct nfs4_sequence_res *seq_res; + int cache_reply; +}; + +static void nfs41_call_sync_prepare(struct rpc_task *task, void *calldata) +{ + struct nfs41_call_sync_data *data = calldata; + + dprintk("--> %s data->clp->cl_session %p\n", __func__, + data->clp->cl_session); + if (nfs4_setup_sequence(data->clp, data->seq_args, + data->seq_res, data->cache_reply, task)) + return; + rpc_call_start(task); +} + +struct rpc_call_ops nfs41_call_sync_ops = { + .rpc_call_prepare = nfs41_call_sync_prepare, +}; + +static int nfs4_call_sync_sequence(struct nfs_client *clp, + struct rpc_clnt *clnt, + struct rpc_message *msg, + struct nfs4_sequence_args *args, + struct nfs4_sequence_res *res, + int cache_reply) +{ + int ret; + struct rpc_task *task; + struct nfs41_call_sync_data data = { + .clp = clp, + .seq_args = args, + .seq_res = res, + .cache_reply = cache_reply, + }; + struct rpc_task_setup task_setup = { + .rpc_client = clnt, + .rpc_message = msg, + .callback_ops = &nfs41_call_sync_ops, + .callback_data = &data + }; + + res->sr_slotid = NFS4_MAX_SLOT_TABLE; + task = rpc_run_task(&task_setup); + if (IS_ERR(task)) + ret = PTR_ERR(task); + else { + ret = task->tk_status; + rpc_put_task(task); + } + return ret; +} + int _nfs4_call_sync_session(struct nfs_server *server, struct rpc_message *msg, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, int cache_reply) { - /* in preparation for setting up the sequence op */ - return rpc_call_sync(server->client, msg, 0); + return nfs4_call_sync_sequence(server->nfs_client, server->client, + msg, args, res, cache_reply); } #endif /* CONFIG_NFS_V4_1 */ -- cgit v1.2.3 From 510b81756f18922a4c5b555e8145f4fed5beb569 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:22:14 -0400 Subject: nfs41: find slot Find a free slot using bitmap-based allocation. Use the optimized ffz function to find a zero bit in the bitmap that indicates a free slot, starting the search from the 'lowest_free_slotid' position. If found, mark the slot as used in the bitmap, get the slot's slotid and seqid, and update max_slotid to be used by the SEQUENCE operation. Also, update lowest_free_slotid for next search. If no free slot was found the caller has to wait for a free slot (outside the scope of this function) Signed-off-by: Benny Halevy [nfs41: find slot return slotid] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [use find_first_zero_bit for nfs4_find_slot as per review comment 21/85.] [use NFS4_MAX_SLOT_TABLE rather than NFS4_NO_SLOT] [nfs41: rpc_sleep_on slot_tbl_waitq must be called under slot_tbl_lock] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 90aed76e593..a0946a0d116 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -273,6 +273,39 @@ static void renew_lease(const struct nfs_server *server, unsigned long timestamp #if defined(CONFIG_NFS_V4_1) +/* + * nfs4_find_slot - efficiently look for a free slot + * + * nfs4_find_slot looks for an unset bit in the used_slots bitmap. + * If found, we mark the slot as used, update the highest_used_slotid, + * and respectively set up the sequence operation args. + * The slot number is returned if found, or NFS4_MAX_SLOT_TABLE otherwise. + */ +static u8 +nfs4_find_slot(struct nfs4_slot_table *tbl, struct rpc_task *task) +{ + int slotid; + u8 ret_id = NFS4_MAX_SLOT_TABLE; + BUILD_BUG_ON((u8)NFS4_MAX_SLOT_TABLE != (int)NFS4_MAX_SLOT_TABLE); + + spin_lock(&tbl->slot_tbl_lock); + dprintk("--> %s used_slots=%04lx highest_used=%d max_slots=%d\n", + __func__, tbl->used_slots[0], tbl->highest_used_slotid, + tbl->max_slots); + slotid = find_first_zero_bit(tbl->used_slots, tbl->max_slots); + if (slotid >= tbl->max_slots) + goto out; + __set_bit(slotid, tbl->used_slots); + if (slotid > tbl->highest_used_slotid) + tbl->highest_used_slotid = slotid; + ret_id = slotid; +out: + dprintk("<-- %s used_slots=%04lx highest_used=%d slotid=%d \n", + __func__, tbl->used_slots[0], tbl->highest_used_slotid, ret_id); + spin_unlock(&tbl->slot_tbl_lock); + return ret_id; +} + static int nfs41_setup_sequence(struct nfs4_session *session, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, -- cgit v1.2.3 From fbcd4abcb3841f85578985c09c6df85aa41b0ae8 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:15 -0400 Subject: nfs41: setup_sequence method Allocate a slot in the session slot table and set the sequence op arguments. Called at the rpc prepare stage. Add a status to nfs41_sequence_res, initialize it to one so that we catch rpc level failures which do not go through decode_sequence which sets the new status field. Note that upon an rpc level failure, we don't know if the server processed the sequence operation or not. Proceed as if the server did process the sequence operation. Signed-off-by: Rahul Iyer [nfs41: sequence args use slotid] [nfs41: find slot return slotid] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: remove SEQ4_STATUS_USE_TK_STATUS] As per 11-14-08 review [move extern declaration from nfs41: sequence setup/done support] [removed sa_session definition, changed sa_cache_this into a u8 to reduce footprint] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: rpc_sleep_on slot_tbl_waitq must be called under slot_tbl_lock] Otherwise there's a race (we've hit) with nfs4_free_slot where nfs41_setup_sequence sees a full slot table, unlocks slot_tbl_lock, nfs4_free_slots happen concurrently and call rpc_wake_up_next where there's nobody to wake up yet, context goes back to nfs41_setup_sequence which goes to sleep when the slot table is actually empty now and there's no-one to wake it up anymore. Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 10 ++++++++++ fs/nfs/nfs4proc.c | 34 ++++++++++++++++++++++++++++++++-- include/linux/nfs_xdr.h | 4 ++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index acac6f8c3d3..eccf4e93e7d 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -203,8 +203,18 @@ extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, extern struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops; extern struct nfs4_state_recovery_ops nfs4_nograce_recovery_ops; #if defined(CONFIG_NFS_V4_1) +extern int nfs4_setup_sequence(struct nfs_client *clp, + struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, + int cache_reply, struct rpc_task *task); extern void nfs4_destroy_session(struct nfs4_session *session); extern struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp); +#else /* CONFIG_NFS_v4_1 */ +static inline int nfs4_setup_sequence(struct nfs_client *clp, + struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, + int cache_reply, struct rpc_task *task) +{ + return 0; +} #endif /* CONFIG_NFS_V4_1 */ extern const u32 nfs4_fattr_bitmap[2]; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a0946a0d116..c9618080317 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -280,6 +280,8 @@ static void renew_lease(const struct nfs_server *server, unsigned long timestamp * If found, we mark the slot as used, update the highest_used_slotid, * and respectively set up the sequence operation args. * The slot number is returned if found, or NFS4_MAX_SLOT_TABLE otherwise. + * + * Note: must be called with under the slot_tbl_lock. */ static u8 nfs4_find_slot(struct nfs4_slot_table *tbl, struct rpc_task *task) @@ -288,7 +290,6 @@ nfs4_find_slot(struct nfs4_slot_table *tbl, struct rpc_task *task) u8 ret_id = NFS4_MAX_SLOT_TABLE; BUILD_BUG_ON((u8)NFS4_MAX_SLOT_TABLE != (int)NFS4_MAX_SLOT_TABLE); - spin_lock(&tbl->slot_tbl_lock); dprintk("--> %s used_slots=%04lx highest_used=%d max_slots=%d\n", __func__, tbl->used_slots[0], tbl->highest_used_slotid, tbl->max_slots); @@ -302,7 +303,6 @@ nfs4_find_slot(struct nfs4_slot_table *tbl, struct rpc_task *task) out: dprintk("<-- %s used_slots=%04lx highest_used=%d slotid=%d \n", __func__, tbl->used_slots[0], tbl->highest_used_slotid, ret_id); - spin_unlock(&tbl->slot_tbl_lock); return ret_id; } @@ -312,12 +312,42 @@ static int nfs41_setup_sequence(struct nfs4_session *session, int cache_reply, struct rpc_task *task) { + struct nfs4_slot *slot; + struct nfs4_slot_table *tbl; + u8 slotid; + + dprintk("--> %s\n", __func__); /* slot already allocated? */ if (res->sr_slotid != NFS4_MAX_SLOT_TABLE) return 0; memset(res, 0, sizeof(*res)); res->sr_slotid = NFS4_MAX_SLOT_TABLE; + tbl = &session->fc_slot_table; + + spin_lock(&tbl->slot_tbl_lock); + slotid = nfs4_find_slot(tbl, task); + if (slotid == NFS4_MAX_SLOT_TABLE) { + rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL); + spin_unlock(&tbl->slot_tbl_lock); + dprintk("<-- %s: no free slots\n", __func__); + return -EAGAIN; + } + spin_unlock(&tbl->slot_tbl_lock); + + slot = tbl->slots + slotid; + args->sa_slotid = slotid; + args->sa_cache_this = cache_reply; + + dprintk("<-- %s slotid=%d seqid=%d\n", __func__, slotid, slot->seq_nr); + + res->sr_slotid = slotid; + res->sr_renewal_time = jiffies; + /* + * sr_status is only set in decode_sequence, and so will remain + * set to 1 if an rpc level failure occurs. + */ + res->sr_status = 1; return 0; } diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index db0d1236aae..4ac14b40efc 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -163,11 +163,15 @@ struct nfs4_slot { struct nfs4_sequence_args { struct nfs4_session *sa_session; + u8 sa_slotid; + u8 sa_cache_this; }; struct nfs4_sequence_res { struct nfs4_session *sr_session; u8 sr_slotid; /* slot used to send request */ + unsigned long sr_renewal_time; + int sr_status; /* sequence operation status */ }; /* -- cgit v1.2.3 From e2c4ab3ce2ecc527672bd7e29a594c50d3ec0477 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:16 -0400 Subject: nfs41: free slot Free a slot in the slot table. Mark the slot as free in the bitmap-based allocation table by clearing a bit corresponding to the slotid. Update lowest_free_slotid if freed slotid is lower than that. Update highest_used_slotid. In the case the freed slotid equals the highest_used_slotid, scan downwards for the next highest used slotid using the optimized fls* functions. Finally, wake up thread waiting on slot_tbl_waitq for a free slot to become available. Signed-off-by: Benny Halevy [nfs41: free slot use slotid] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: use find_first_zero_bit for nfs4_find_slot] While at it, obliterate lowest_free_slotid and fix-up related comments. As per review comment 21/85. Signed-off-by: Benny Halevy [nfs41: use __clear_bit for nfs4_free_slot] While at it, fix-up function comment. Part of review comment 22/85. Signed-off-by: Benny Halevy [nfs41: use find_last_bit in nfs4_free_slot to determine highest used slot.] Signed-off-by: Benny Halevy [nfs41: rpc_sleep_on slot_tbl_waitq must be called under slot_tbl_lock] Otherwise there's a race (we've hit) with nfs4_free_slot where nfs41_setup_sequence sees a full slot table, unlocks slot_tbl_lock, nfs4_free_slots happen concurrently and call rpc_wake_up_next where there's nobody to wake up yet, context goes back to nfs41_setup_sequence which goes to sleep when the slot table is actually empty now and there's no-one to wake it up anymore. Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index c9618080317..b6308f6740e 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -273,6 +273,42 @@ static void renew_lease(const struct nfs_server *server, unsigned long timestamp #if defined(CONFIG_NFS_V4_1) +/* + * nfs4_free_slot - free a slot and efficiently update slot table. + * + * freeing a slot is trivially done by clearing its respective bit + * in the bitmap. + * If the freed slotid equals highest_used_slotid we want to update it + * so that the server would be able to size down the slot table if needed, + * otherwise we know that the highest_used_slotid is still in use. + * When updating highest_used_slotid there may be "holes" in the bitmap + * so we need to scan down from highest_used_slotid to 0 looking for the now + * highest slotid in use. + * If none found, highest_used_slotid is set to -1. + */ +static void +nfs4_free_slot(struct nfs4_slot_table *tbl, u8 free_slotid) +{ + int slotid = free_slotid; + + spin_lock(&tbl->slot_tbl_lock); + /* clear used bit in bitmap */ + __clear_bit(slotid, tbl->used_slots); + + /* update highest_used_slotid when it is freed */ + if (slotid == tbl->highest_used_slotid) { + slotid = find_last_bit(tbl->used_slots, tbl->max_slots); + if (slotid >= 0 && slotid < tbl->max_slots) + tbl->highest_used_slotid = slotid; + else + tbl->highest_used_slotid = -1; + } + rpc_wake_up_next(&tbl->slot_tbl_waitq); + spin_unlock(&tbl->slot_tbl_lock); + dprintk("%s: free_slotid %u highest_used_slotid %d\n", __func__, + free_slotid, tbl->highest_used_slotid); +} + /* * nfs4_find_slot - efficiently look for a free slot * -- cgit v1.2.3 From 13615871cdf8e8263626a817c350c6978a6483fe Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:17 -0400 Subject: nfs41: nfs41_sequence_free_slot [from nfs41: separate free slot from sequence done] Don't free the slot until after all rpc_restart_calls have completed. Session reset will require more work. As noted by Trond, since we're using rpc_wake_up_next rather than rpc_wake_up() we must always wake up the next task in the queue either by going through nfs4_free_slot, or just calling rpc_wake_up_next if no slot is to be freed. [nfs41: sequence res use slotid] [nfs41: remove SEQ4_STATUS_USE_TK_STATUS] [got rid of nfs4_sequence_res.sr_session, use nfs_client.cl_session instead] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: rpc_wake_up_next if sessions slot was not consumed.] Signed-off-by: Benny Halevy [nfs41: nfs4_sequence_free_slot use nfs_client for data server] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/internal.h | 14 ++++++++++++++ fs/nfs/nfs4proc.c | 21 +++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 8d67c2865dc..12f4c5e6fd3 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -230,6 +230,20 @@ static inline int nfs4_has_session(const struct nfs_client *clp) return 0; } +#ifdef CONFIG_NFS_V4_1 +extern void nfs41_sequence_free_slot(const struct nfs_client *, + struct nfs4_sequence_res *res); +#endif /* CONFIG_NFS_V4_1 */ + +static inline void nfs4_sequence_free_slot(const struct nfs_client *clp, + struct nfs4_sequence_res *res) +{ +#ifdef CONFIG_NFS_V4_1 + if (nfs4_has_session(clp)) + nfs41_sequence_free_slot(clp, res); +#endif /* CONFIG_NFS_V4_1 */ +} + /* * Determine the device name as a string */ diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index b6308f6740e..861b103c9e3 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -309,6 +309,27 @@ nfs4_free_slot(struct nfs4_slot_table *tbl, u8 free_slotid) free_slotid, tbl->highest_used_slotid); } +void nfs41_sequence_free_slot(const struct nfs_client *clp, + struct nfs4_sequence_res *res) +{ + struct nfs4_slot_table *tbl; + + if (!nfs4_has_session(clp)) { + dprintk("%s: No session\n", __func__); + return; + } + tbl = &clp->cl_session->fc_slot_table; + if (res->sr_slotid == NFS4_MAX_SLOT_TABLE) { + dprintk("%s: No slot\n", __func__); + /* just wake up the next guy waiting since + * we may have not consumed a slot after all */ + rpc_wake_up_next(&tbl->slot_tbl_waitq); + return; + } + nfs4_free_slot(tbl, res->sr_slotid); + res->sr_slotid = NFS4_MAX_SLOT_TABLE; +} + /* * nfs4_find_slot - efficiently look for a free slot * -- cgit v1.2.3 From b0df806c0f9d61e5fcfdb0f4e606e1a59303aaa6 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:18 -0400 Subject: nfs41: nfs41_sequence_done Handle session level errors, update slot sequence id and sessions bookeeping, free slot. [nfs41: sequence res use slotid] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: remove SEQ4_STATUS_USE_TK_STATUS] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: check for session not minorversion] Signed-off-by: Andy Adamson [nfs41: bail out early out of nfs41_sequence_done if !res->sr_session] Signed-off-by: Benny Halevy [move nfs4_sequence_done from nfs41: nfs41_call_sync_done] Signed-off-by: Andy Adamson [move nfs4_sequence_free_slot from nfs41: separate free slot from sequence done] Don't free the slot until after all rpc_restart_calls have completed. Session reset will require more work. Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [moved reset sr_slotid to nfs41_sequence_free_slot] [free slot also on unexpectecd error] [remove seq_res.sr_session member, use nfs_client's instead] [ditch seq_res.sr_flags until used] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [look at sr_slotid for bailing out early from nfs41_sequence_done] [nfs41: rpc_wake_up_next if sessions slot was not consumed.] Signed-off-by: Benny Halevy [nfs41: nfs4_sequence_free_slot use nfs_client for data server] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: remove unused error checking in nfs41_sequence_done] Signed-off-by: Andy Adamson [nfs41: remove nfs4_has_session check in nfs41_sequence_done] Signed-off-by: Andy Adamson [nfs41: remove nfs_client pointer check] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 861b103c9e3..188f2ce593e 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -330,6 +330,46 @@ void nfs41_sequence_free_slot(const struct nfs_client *clp, res->sr_slotid = NFS4_MAX_SLOT_TABLE; } +static void nfs41_sequence_done(struct nfs_client *clp, + struct nfs4_sequence_res *res, + int rpc_status) +{ + unsigned long timestamp; + struct nfs4_slot_table *tbl; + struct nfs4_slot *slot; + + /* + * sr_status remains 1 if an RPC level error occurred. The server + * may or may not have processed the sequence operation.. + * Proceed as if the server received and processed the sequence + * operation. + */ + if (res->sr_status == 1) + res->sr_status = NFS_OK; + + /* -ERESTARTSYS can result in skipping nfs41_sequence_setup */ + if (res->sr_slotid == NFS4_MAX_SLOT_TABLE) + goto out; + + tbl = &clp->cl_session->fc_slot_table; + slot = tbl->slots + res->sr_slotid; + + if (res->sr_status == 0) { + /* Update the slot's sequence and clientid lease timer */ + ++slot->seq_nr; + timestamp = res->sr_renewal_time; + spin_lock(&clp->cl_lock); + if (time_before(clp->cl_last_renewal, timestamp)) + clp->cl_last_renewal = timestamp; + spin_unlock(&clp->cl_lock); + return; + } +out: + /* The session may be reset by one of the error handlers. */ + dprintk("%s: Error %d free the slot \n", __func__, res->sr_status); + nfs41_sequence_free_slot(clp, res); +} + /* * nfs4_find_slot - efficiently look for a free slot * @@ -515,6 +555,24 @@ int _nfs4_call_sync(struct nfs_server *server, (server)->nfs_client->cl_call_sync((server), (msg), &(args)->seq_args, \ &(res)->seq_res, (cache_reply)) +static void nfs4_sequence_done(const struct nfs_server *server, + struct nfs4_sequence_res *res, int rpc_status) +{ +#ifdef CONFIG_NFS_V4_1 + if (nfs4_has_session(server->nfs_client)) + nfs41_sequence_done(server->nfs_client, res, rpc_status); +#endif /* CONFIG_NFS_V4_1 */ +} + +/* no restart, therefore free slot here */ +static void nfs4_sequence_done_free_slot(const struct nfs_server *server, + struct nfs4_sequence_res *res, + int rpc_status) +{ + nfs4_sequence_done(server, res, rpc_status); + nfs4_sequence_free_slot(server->nfs_client, res); +} + static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo) { struct nfs_inode *nfsi = NFS_I(dir); -- cgit v1.2.3 From 69ab40c4c3b9622232e31e0ee145875a2853c113 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:19 -0400 Subject: nfs41: nfs41_call_sync_done Implement nfs4.1 synchronous rpc_call_done method that essentially just calls nfs4_sequence_done, that turns around and calls nfs41_sequence_done for minorversion1 rpcs. Signed-off-by: Benny Halevy [nfs41: check for session not minorversion] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [move adding nfs4_sequence_free_slot from nfs41-separate-free-slot-from-sequence-done] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: nfs41_call_sync_data use nfs_client not nfs_server] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 188f2ce593e..a8e0b357359 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -492,8 +492,17 @@ static void nfs41_call_sync_prepare(struct rpc_task *task, void *calldata) rpc_call_start(task); } +static void nfs41_call_sync_done(struct rpc_task *task, void *calldata) +{ + struct nfs41_call_sync_data *data = calldata; + + nfs41_sequence_done(data->clp, data->seq_res, task->tk_status); + nfs41_sequence_free_slot(data->clp, data->seq_res); +} + struct rpc_call_ops nfs41_call_sync_ops = { .rpc_call_prepare = nfs41_call_sync_prepare, + .rpc_call_done = nfs41_call_sync_done, }; static int nfs4_call_sync_sequence(struct nfs_client *clp, -- cgit v1.2.3 From 19ddab06ed923d27339b8009d37b0ab3de93cadf Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:20 -0400 Subject: nfs41: close sequence setup/done support Separate nfs4_close calls from nfs41: sequence setup/done support Call nfs4_sequence_done from respective rpc_call_done methods. Note that we need to pass a pointer to the nfs_server in calls data for passing on to nfs4_sequence_done. Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [pnfs: client data server write validate and release] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: separate free slot from sequence done] [nfs41: sequence res use slotid] [nfs41: remove SEQ4_STATUS_USE_TK_STATUS] [nfs41: nfs4_sequence_free_slot use nfs_client for data server] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a8e0b357359..a34fada5144 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1630,6 +1630,7 @@ static void nfs4_close_done(struct rpc_task *task, void *data) struct nfs4_state *state = calldata->state; struct nfs_server *server = NFS_SERVER(calldata->inode); + nfs4_sequence_done(server, &calldata->res.seq_res, task->tk_status); if (RPC_ASSASSINATED(task)) return; /* hmm. we are done with the inode, and in the process of freeing @@ -1652,6 +1653,7 @@ static void nfs4_close_done(struct rpc_task *task, void *data) return; } } + nfs4_sequence_free_slot(server->nfs_client, &calldata->res.seq_res); nfs_refresh_inode(calldata->inode, calldata->res.fattr); } @@ -1692,6 +1694,10 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data) calldata->arg.fmode = FMODE_WRITE; } calldata->timestamp = jiffies; + if (nfs4_setup_sequence((NFS_SERVER(calldata->inode))->nfs_client, + &calldata->arg.seq_args, &calldata->res.seq_res, + 1, task)) + return; rpc_call_start(task); } @@ -1731,7 +1737,7 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait) }; int status = -ENOMEM; - calldata = kmalloc(sizeof(*calldata), GFP_KERNEL); + calldata = kzalloc(sizeof(*calldata), GFP_KERNEL); if (calldata == NULL) goto out; calldata->inode = state->inode; -- cgit v1.2.3 From d898528cdb338b145c71049c3cb7b1dfa542a48a Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:21 -0400 Subject: nfs41: open sequence setup/done support Separate nfs4_open calls from nfs41: sequence setup/done support Call nfs4_sequence_done from respective rpc_call_done methods. Note that we need to pass a pointer to the nfs_server in calls data for passing on to nfs4_sequence_done. Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [pnfs: client data server write validate and release] [use nfs4_sequence_done_free_slot] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a34fada5144..46e2509452a 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1241,6 +1241,10 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata) nfs_copy_fh(&data->o_res.fh, data->o_arg.fh); } data->timestamp = jiffies; + if (nfs4_setup_sequence(data->o_arg.server->nfs_client, + &data->o_arg.seq_args, + &data->o_res.seq_res, 1, task)) + return; rpc_call_start(task); return; out_no_action: @@ -1253,6 +1257,10 @@ static void nfs4_open_done(struct rpc_task *task, void *calldata) struct nfs4_opendata *data = calldata; data->rpc_status = task->tk_status; + + nfs4_sequence_done_free_slot(data->o_arg.server, &data->o_res.seq_res, + task->tk_status); + if (RPC_ASSASSINATED(task)) return; if (task->tk_status == 0) { -- cgit v1.2.3 From 66179efee31f431ab538016b4ac05edaf87525dc Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:22 -0400 Subject: nfs41: lock sequence setup/done support Separate nfs4_lock calls from nfs41: sequence setup/done support Call nfs4_sequence_done from respective rpc_call_done methods. Note that we need to pass a pointer to the nfs_server in calls data for passing on to nfs4_sequence_done. Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [pnfs: client data server write validate and release] [use nfs4_sequence_done_free_slot] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 46e2509452a..dc070819096 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3688,6 +3688,7 @@ struct nfs4_lockdata { unsigned long timestamp; int rpc_status; int cancelled; + struct nfs_server *server; }; static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl, @@ -3715,6 +3716,7 @@ static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl, p->res.lock_seqid = p->arg.lock_seqid; p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; p->lsp = lsp; + p->server = server; atomic_inc(&lsp->ls_count); p->ctx = get_nfs_open_context(ctx); memcpy(&p->fl, fl, sizeof(p->fl)); @@ -3744,6 +3746,9 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata) } else data->arg.new_lock_owner = 0; data->timestamp = jiffies; + if (nfs4_setup_sequence(data->server->nfs_client, &data->arg.seq_args, + &data->res.seq_res, 1, task)) + return; rpc_call_start(task); dprintk("%s: done!, ret = %d\n", __func__, data->rpc_status); } @@ -3754,6 +3759,9 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata) dprintk("%s: begin!\n", __func__); + nfs4_sequence_done_free_slot(data->server, &data->res.seq_res, + task->tk_status); + data->rpc_status = task->tk_status; if (RPC_ASSASSINATED(task)) goto out; -- cgit v1.2.3 From a893693c151433a1375060ecb13419080426bc08 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:23 -0400 Subject: nfs41: locku sequence setup/done support Separate nfs4_locku calls from nfs41: sequence setup/done support Call nfs4_sequence_done from respective rpc_call_done methods. Note that we need to pass a pointer to the nfs_server in calls data for passing on to nfs4_sequence_done. Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [pnfs: client data server write validate and release] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: nfs4_sequence_free_slot use nfs_client for data server] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index dc070819096..d2cfa4f8653 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3533,7 +3533,7 @@ static struct nfs4_unlockdata *nfs4_alloc_unlockdata(struct file_lock *fl, struct nfs4_unlockdata *p; struct inode *inode = lsp->ls_state->inode; - p = kmalloc(sizeof(*p), GFP_KERNEL); + p = kzalloc(sizeof(*p), GFP_KERNEL); if (p == NULL) return NULL; p->arg.fh = NFS_FH(inode); @@ -3564,6 +3564,8 @@ static void nfs4_locku_done(struct rpc_task *task, void *data) { struct nfs4_unlockdata *calldata = data; + nfs4_sequence_done(calldata->server, &calldata->res.seq_res, + task->tk_status); if (RPC_ASSASSINATED(task)) return; switch (task->tk_status) { @@ -3582,6 +3584,8 @@ static void nfs4_locku_done(struct rpc_task *task, void *data) if (nfs4_async_handle_error(task, calldata->server, NULL) == -EAGAIN) rpc_restart_call(task); } + nfs4_sequence_free_slot(calldata->server->nfs_client, + &calldata->res.seq_res); } static void nfs4_locku_prepare(struct rpc_task *task, void *data) @@ -3596,6 +3600,10 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data) return; } calldata->timestamp = jiffies; + if (nfs4_setup_sequence(calldata->server->nfs_client, + &calldata->arg.seq_args, + &calldata->res.seq_res, 1, task)) + return; rpc_call_start(task); } -- cgit v1.2.3 From 472cfbd9b97be210a9b61ac1c6a774cd456ea9d2 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:24 -0400 Subject: nfs41: unlink sequence setup/done support Implement the rpc_call_prepare methods for asynchronuos nfs rpcs, call nfs41_setup_sequence from respective rpc_call_validate_args methods. Call nfs4_sequence_done from respective rpc_call_done methods. Note that we need to pass a pointer to the nfs_server in calls data for passing on to nfs4_sequence_done. Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [pnfs: client data server write validate and release] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: separate free slot from sequence done] [nfs41: sequence res use slotid] [nfs41: remove SEQ4_STATUS_USE_TK_STATUS] [nfs41: nfs4_sequence_free_slot use nfs_client for data server] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 2 ++ fs/nfs/unlink.c | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index d2cfa4f8653..97521e47e43 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2424,8 +2424,10 @@ static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir) { struct nfs_removeres *res = task->tk_msg.rpc_resp; + nfs4_sequence_done(res->server, &res->seq_res, task->tk_status); if (nfs4_async_handle_error(task, res->server, NULL) == -EAGAIN) return 0; + nfs4_sequence_free_slot(res->server->nfs_client, &res->seq_res); update_changeattr(dir, &res->cinfo); nfs_post_op_update_inode(dir, &res->dir_attr); return 1; diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index 83d0ce2600a..089a21b4e3f 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c @@ -15,6 +15,7 @@ #include #include "internal.h" +#include "nfs4_fs.h" struct nfs_unlinkdata { struct hlist_node list; @@ -102,9 +103,25 @@ static void nfs_async_unlink_release(void *calldata) nfs_sb_deactive(sb); } +#if defined(CONFIG_NFS_V4_1) +void nfs_unlink_prepare(struct rpc_task *task, void *calldata) +{ + struct nfs_unlinkdata *data = calldata; + struct nfs_server *server = NFS_SERVER(data->dir); + + if (nfs4_setup_sequence(server->nfs_client, &data->args.seq_args, + &data->res.seq_res, 1, task)) + return; + rpc_call_start(task); +} +#endif /* CONFIG_NFS_V4_1 */ + static const struct rpc_call_ops nfs_unlink_ops = { .rpc_call_done = nfs_async_unlink_done, .rpc_release = nfs_async_unlink_release, +#if defined(CONFIG_NFS_V4_1) + .rpc_call_prepare = nfs_unlink_prepare, +#endif /* CONFIG_NFS_V4_1 */ }; static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct nfs_unlinkdata *data) -- cgit v1.2.3 From f11c88af26453aee2823a1fd9120d0cd8dae7b9a Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:25 -0400 Subject: nfs41: read sequence setup/done support Implement the read rpc_call_prepare method for asynchronuos nfs rpcs, call nfs41_setup_sequence from respective rpc_call_validate_args methods. Call nfs4_sequence_done from respective rpc_call_done methods. Note that we need to pass a pointer to the nfs_server in calls data for passing on to nfs4_sequence_done. Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [pnfs: client data server write validate and release] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [move the nfs4_sequence_free_slot call in nfs_readpage_retry from] [nfs41: separate free slot from sequence done] [remove nfs_readargs.nfs_server, use calldata->inode instead] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: Support sessions with O_DIRECT] Signed-off-by: Dean Hildebrand Signed-off-by: Benny Halevy [nfs41: nfs4_sequence_free_slot use nfs_client for data server] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/direct.c | 3 +++ fs/nfs/internal.h | 3 +++ fs/nfs/nfs4proc.c | 5 +++++ fs/nfs/read.c | 30 ++++++++++++++++++++++++++++-- 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 08f6b040d28..c8c53a03a58 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -259,6 +259,9 @@ static void nfs_direct_read_release(void *calldata) } static const struct rpc_call_ops nfs_read_direct_ops = { +#if defined(CONFIG_NFS_V4_1) + .rpc_call_prepare = nfs_read_prepare, +#endif /* CONFIG_NFS_V4_1 */ .rpc_call_done = nfs_direct_read_result, .rpc_release = nfs_direct_read_release, }; diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 12f4c5e6fd3..988c8b9aa78 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -206,6 +206,9 @@ extern int nfs4_path_walk(struct nfs_server *server, const char *path); #endif +/* read.c */ +extern void nfs_read_prepare(struct rpc_task *task, void *calldata); + /* nfs4proc.c */ extern int _nfs4_call_sync(struct nfs_server *server, struct rpc_message *msg, diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 97521e47e43..6c0ac539d9c 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2864,6 +2864,11 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data) { struct nfs_server *server = NFS_SERVER(data->inode); + dprintk("--> %s\n", __func__); + + /* 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, data->args.context->state) == -EAGAIN) { rpc_restart_call(task); return -EAGAIN; diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 70ba2b4cb9a..d71f0d55ebd 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -22,6 +22,7 @@ #include +#include "nfs4_fs.h" #include "internal.h" #include "iostat.h" #include "fscache.h" @@ -358,19 +359,25 @@ static void nfs_readpage_retry(struct rpc_task *task, struct nfs_read_data *data struct nfs_readres *resp = &data->res; if (resp->eof || resp->count == argp->count) - return; + goto out; /* This is a short read! */ nfs_inc_stats(data->inode, NFSIOS_SHORTREAD); /* Has the server at least made some progress? */ if (resp->count == 0) - return; + goto out; /* Yes, so retry the read at the end of the data */ argp->offset += resp->count; argp->pgbase += resp->count; argp->count -= resp->count; rpc_restart_call(task); + return; +out: + nfs4_sequence_free_slot(NFS_SERVER(data->inode)->nfs_client, + &data->res.seq_res); + return; + } /* @@ -407,7 +414,23 @@ static void nfs_readpage_release_partial(void *calldata) nfs_readdata_release(calldata); } +#if defined(CONFIG_NFS_V4_1) +void nfs_read_prepare(struct rpc_task *task, void *calldata) +{ + struct nfs_read_data *data = calldata; + + if (nfs4_setup_sequence(NFS_SERVER(data->inode)->nfs_client, + &data->args.seq_args, &data->res.seq_res, + 0, task)) + return; + rpc_call_start(task); +} +#endif /* CONFIG_NFS_V4_1 */ + static const struct rpc_call_ops nfs_read_partial_ops = { +#if defined(CONFIG_NFS_V4_1) + .rpc_call_prepare = nfs_read_prepare, +#endif /* CONFIG_NFS_V4_1 */ .rpc_call_done = nfs_readpage_result_partial, .rpc_release = nfs_readpage_release_partial, }; @@ -471,6 +494,9 @@ static void nfs_readpage_release_full(void *calldata) } static const struct rpc_call_ops nfs_read_full_ops = { +#if defined(CONFIG_NFS_V4_1) + .rpc_call_prepare = nfs_read_prepare, +#endif /* CONFIG_NFS_V4_1 */ .rpc_call_done = nfs_readpage_result_full, .rpc_release = nfs_readpage_release_full, }; -- cgit v1.2.3 From def6ed7ef45ed19c3d6ca765f3bfdff1fe4c6bba Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:26 -0400 Subject: nfs41 write sequence setup done support Separate write calls from nfs41: sequence setup/done support Implement the write rpc_call_prepare method for asynchronuos nfs rpcs, call nfs41_setup_sequence from respective rpc_call_validate_args methods. Call nfs4_sequence_done from respective rpc_call_done methods. Note that we need to pass a pointer to the nfs_server in calls data for passing on to nfs4_sequence_done. Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [pnfs: client data server write validate and release] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [move the nfs4_sequence_free_slot call in nfs_readpage_retry from] [nfs41: separate free slot from sequence done Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: Support sessions with O_DIRECT.] Signed-off-by: Dean Hildebrand Signed-off-by: Benny Halevy [nfs41: nfs4_sequence_free_slot use nfs_client for data server] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/direct.c | 3 +++ fs/nfs/internal.h | 3 +++ fs/nfs/nfs4proc.c | 4 ++++ fs/nfs/write.c | 22 ++++++++++++++++++++++ 4 files changed, 32 insertions(+) diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index c8c53a03a58..1955bfeb355 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -676,6 +676,9 @@ out_unlock: } static const struct rpc_call_ops nfs_write_direct_ops = { +#if defined(CONFIG_NFS_V4_1) + .rpc_call_prepare = nfs_write_prepare, +#endif /* CONFIG_NFS_V4_1 */ .rpc_call_done = nfs_direct_write_result, .rpc_release = nfs_direct_write_release, }; diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 988c8b9aa78..f62bc522615 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -209,6 +209,9 @@ extern int nfs4_path_walk(struct nfs_server *server, /* read.c */ extern void nfs_read_prepare(struct rpc_task *task, void *calldata); +/* write.c */ +extern void nfs_write_prepare(struct rpc_task *task, void *calldata); + /* nfs4proc.c */ extern int _nfs4_call_sync(struct nfs_server *server, struct rpc_message *msg, diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 6c0ac539d9c..5f71832436f 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2890,6 +2890,10 @@ static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data) { struct inode *inode = data->inode; + /* slot is freed in nfs_writeback_done */ + nfs4_sequence_done(NFS_SERVER(inode), &data->res.seq_res, + task->tk_status); + if (nfs4_async_handle_error(task, NFS_SERVER(inode), data->args.context->state) == -EAGAIN) { rpc_restart_call(task); return -EAGAIN; diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 035e6fb9f57..195fe766752 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -25,6 +25,7 @@ #include "delegation.h" #include "internal.h" #include "iostat.h" +#include "nfs4_fs.h" #define NFSDBG_FACILITY NFSDBG_PAGECACHE @@ -1050,7 +1051,23 @@ out: nfs_writedata_release(calldata); } +#if defined(CONFIG_NFS_V4_1) +void nfs_write_prepare(struct rpc_task *task, void *calldata) +{ + struct nfs_write_data *data = calldata; + struct nfs_client *clp = (NFS_SERVER(data->inode))->nfs_client; + + if (nfs4_setup_sequence(clp, &data->args.seq_args, + &data->res.seq_res, 1, task)) + return; + rpc_call_start(task); +} +#endif /* CONFIG_NFS_V4_1 */ + static const struct rpc_call_ops nfs_write_partial_ops = { +#if defined(CONFIG_NFS_V4_1) + .rpc_call_prepare = nfs_write_prepare, +#endif /* CONFIG_NFS_V4_1 */ .rpc_call_done = nfs_writeback_done_partial, .rpc_release = nfs_writeback_release_partial, }; @@ -1113,6 +1130,9 @@ remove_request: } static const struct rpc_call_ops nfs_write_full_ops = { +#if defined(CONFIG_NFS_V4_1) + .rpc_call_prepare = nfs_write_prepare, +#endif /* CONFIG_NFS_V4_1 */ .rpc_call_done = nfs_writeback_done_full, .rpc_release = nfs_writeback_release_full, }; @@ -1195,6 +1215,8 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) /* Can't do anything about it except throw an error. */ task->tk_status = -EIO; } + nfs4_sequence_free_slot(NFS_SERVER(data->inode)->nfs_client, + &data->res.seq_res); return 0; } -- cgit v1.2.3 From 21d9a851aaa4161a9fddde720594659f3dae7fdd Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:27 -0400 Subject: nfs41 commit sequence setup done support Separate commit calls from nfs41: sequence setup/done support Implement the commit rpc_call_prepare method for asynchronuos nfs rpcs, call nfs41_setup_sequence from respective rpc_call_validate_args methods. Call nfs4_sequence_done from respective rpc_call_done methods. Note that we need to pass a pointer to the nfs_server in calls data for passing on to nfs4_sequence_done. Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [pnfs: client data server write validate and release] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: Support sessions with O_DIRECT.] Signed-off-by: Dean Hildebrand Signed-off-by: Benny Halevy [nfs41: separate free slot from sequence done] [nfs41: nfs4_sequence_free_slot use nfs_client for data server] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/direct.c | 3 +++ fs/nfs/nfs4proc.c | 4 ++++ fs/nfs/write.c | 3 +++ 3 files changed, 10 insertions(+) diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 1955bfeb355..489fc01a320 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -538,6 +538,9 @@ static void nfs_direct_commit_release(void *calldata) } static const struct rpc_call_ops nfs_commit_direct_ops = { +#if defined(CONFIG_NFS_V4_1) + .rpc_call_prepare = nfs_write_prepare, +#endif /* CONFIG_NFS_V4_1 */ .rpc_call_done = nfs_direct_commit_result, .rpc_release = nfs_direct_commit_release, }; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 5f71832436f..fbd6274f14f 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2920,10 +2920,14 @@ static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data) { struct inode *inode = data->inode; + nfs4_sequence_done(NFS_SERVER(inode), &data->res.seq_res, + task->tk_status); if (nfs4_async_handle_error(task, NFS_SERVER(inode), NULL) == -EAGAIN) { rpc_restart_call(task); return -EAGAIN; } + nfs4_sequence_free_slot(NFS_SERVER(inode)->nfs_client, + &data->res.seq_res); nfs_refresh_inode(inode, data->res.fattr); return 0; } diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 195fe766752..85a76409de1 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -1373,6 +1373,9 @@ static void nfs_commit_release(void *calldata) } static const struct rpc_call_ops nfs_commit_ops = { +#if defined(CONFIG_NFS_V4_1) + .rpc_call_prepare = nfs_write_prepare, +#endif /* CONFIG_NFS_V4_1 */ .rpc_call_done = nfs_commit_done, .rpc_release = nfs_commit_release, }; -- cgit v1.2.3 From 938e10109115a71cc69d475122f21cf75e5046cd Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:28 -0400 Subject: nfs41 delegreturn sequence setup done support Separate delegreturn calls from nfs41: sequence setup/done support Implement the delegreturn rpc_call_prepare method for asynchronuos nfs rpcs, call nfs41_setup_sequence from respective rpc_call_validate_args methods. Call nfs4_sequence_done from respective rpc_call_done methods. Note that we need to pass a pointer to the nfs_server in calls data for passing on to nfs4_sequence_done. Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [pnfs: client data server write validate and release] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index fbd6274f14f..dc0feb5837b 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3360,6 +3360,10 @@ struct nfs4_delegreturndata { static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata) { struct nfs4_delegreturndata *data = calldata; + + nfs4_sequence_done_free_slot(data->res.server, &data->res.seq_res, + task->tk_status); + data->rpc_status = task->tk_status; if (data->rpc_status == 0) renew_lease(data->res.server, data->timestamp); @@ -3370,7 +3374,25 @@ static void nfs4_delegreturn_release(void *calldata) kfree(calldata); } +#if defined(CONFIG_NFS_V4_1) +static void nfs4_delegreturn_prepare(struct rpc_task *task, void *data) +{ + struct nfs4_delegreturndata *d_data; + + d_data = (struct nfs4_delegreturndata *)data; + + if (nfs4_setup_sequence(d_data->res.server->nfs_client, + &d_data->args.seq_args, + &d_data->res.seq_res, 1, task)) + return; + rpc_call_start(task); +} +#endif /* CONFIG_NFS_V4_1 */ + static const struct rpc_call_ops nfs4_delegreturn_ops = { +#if defined(CONFIG_NFS_V4_1) + .rpc_call_prepare = nfs4_delegreturn_prepare, +#endif /* CONFIG_NFS_V4_1 */ .rpc_call_done = nfs4_delegreturn_done, .rpc_release = nfs4_delegreturn_release, }; @@ -3392,7 +3414,7 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co }; int status = 0; - data = kmalloc(sizeof(*data), GFP_KERNEL); + data = kzalloc(sizeof(*data), GFP_KERNEL); if (data == NULL) return -ENOMEM; data->args.fhandle = &data->fh; -- cgit v1.2.3 From 99fe60d062cfecf382c036065b3278b82b6c5eff Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:22:29 -0400 Subject: nfs41: exchange_id operation Implement the exchange_id operation conforming to http://tools.ietf.org/html/draft-ietf-nfsv4-minorversion1-26 Unlike NFSv4.0, NFSv4.1 requires machine credentials. RPC_AUTH_GSS machine credentials will be passed into the kernel at mount time to be available for the exchange_id operation. RPC_AUTH_UNIX root mounts can use the UNIX root credential. Store the root credential in the nfs_client struct. Without a credential, NFSv4.1 state renewal fails. [nfs41: establish clientid via exchange id only if cred != NULL] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfsd41: move nfstime4 from under CONFIG_NFS_V4_1] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: do not wait a lease time in exchange id] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: pass *session in seq_args and seq_res] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust [nfs41: Ignoring impid in decode_exchange_id is missing a READ_BUF] Signed-off-by: Benny Halevy [nfs41: fix Xcode_exchange_id's xdr Xcoding pointer type] [nfs41: get rid of unused struct nfs41_exchange_id_res members] Signed-off-by: Benny Halevy --- fs/nfs/nfs4proc.c | 61 ++++++++++++++++++++ fs/nfs/nfs4xdr.c | 139 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/nfs4.h | 1 + include/linux/nfs_fs_sb.h | 6 ++ include/linux/nfs_xdr.h | 40 +++++++++++++ include/linux/nfsd/state.h | 1 - 6 files changed, 247 insertions(+), 1 deletion(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index dc0feb5837b..6f384e29075 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -48,6 +48,7 @@ #include #include #include +#include #include "nfs4_fs.h" #include "delegation.h" @@ -433,11 +434,13 @@ static int nfs41_setup_sequence(struct nfs4_session *session, spin_unlock(&tbl->slot_tbl_lock); slot = tbl->slots + slotid; + args->sa_session = session; args->sa_slotid = slotid; args->sa_cache_this = cache_reply; dprintk("<-- %s slotid=%d seqid=%d\n", __func__, slotid, slot->seq_nr); + res->sr_session = session; res->sr_slotid = slotid; res->sr_renewal_time = jiffies; /* @@ -4128,6 +4131,64 @@ int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, } #ifdef CONFIG_NFS_V4_1 +/* + * nfs4_proc_exchange_id() + * + * Since the clientid has expired, all compounds using sessions + * associated with the stale clientid will be returning + * NFS4ERR_BADSESSION in the sequence operation, and will therefore + * be in some phase of session reset. + */ +static int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred) +{ + nfs4_verifier verifier; + struct nfs41_exchange_id_args args = { + .client = clp, + .flags = clp->cl_exchange_flags, + }; + struct nfs41_exchange_id_res res = { + .client = clp, + }; + int status; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_EXCHANGE_ID], + .rpc_argp = &args, + .rpc_resp = &res, + .rpc_cred = cred, + }; + __be32 *p; + + dprintk("--> %s\n", __func__); + BUG_ON(clp == NULL); + p = (u32 *)verifier.data; + *p++ = htonl((u32)clp->cl_boot_time.tv_sec); + *p = htonl((u32)clp->cl_boot_time.tv_nsec); + args.verifier = &verifier; + + while (1) { + args.id_len = scnprintf(args.id, sizeof(args.id), + "%s/%s %u", + clp->cl_ipaddr, + rpc_peeraddr2str(clp->cl_rpcclient, + RPC_DISPLAY_ADDR), + clp->cl_id_uniquifier); + + status = rpc_call_sync(clp->cl_rpcclient, &msg, 0); + + if (status != NFS4ERR_CLID_INUSE) + break; + + if (signalled()) + break; + + if (++clp->cl_id_uniquifier == 0) + break; + } + + dprintk("<-- %s status= %d\n", __func__, status); + return status; +} + /* Destroy the slot table */ static void nfs4_destroy_slot_table(struct nfs4_session *session) { diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 5b944cd5721..783c4214dcc 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -246,6 +246,27 @@ static int nfs4_stat_to_errno(int); (0) #if defined(CONFIG_NFS_V4_1) +#define encode_exchange_id_maxsz (op_encode_hdr_maxsz + \ + encode_verifier_maxsz + \ + 1 /* co_ownerid.len */ + \ + XDR_QUADLEN(NFS4_EXCHANGE_ID_LEN) + \ + 1 /* flags */ + \ + 1 /* spa_how */ + \ + 0 /* SP4_NONE (for now) */ + \ + 1 /* zero implemetation id array */) +#define decode_exchange_id_maxsz (op_decode_hdr_maxsz + \ + 2 /* eir_clientid */ + \ + 1 /* eir_sequenceid */ + \ + 1 /* eir_flags */ + \ + 1 /* spr_how */ + \ + 0 /* SP4_NONE (for now) */ + \ + 2 /* eir_server_owner.so_minor_id */ + \ + /* eir_server_owner.so_major_id<> */ \ + XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1 + \ + /* eir_server_scope<> */ \ + XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1 + \ + 1 /* eir_server_impl_id array length */ + \ + 0 /* ignored eir_server_impl_id contents */) #define encode_sequence_maxsz 0 /* stub */ #define decode_sequence_maxsz 0 /* stub */ #else /* CONFIG_NFS_V4_1 */ @@ -594,6 +615,14 @@ static int nfs4_stat_to_errno(int); decode_putfh_maxsz + \ decode_lookup_maxsz + \ decode_fs_locations_maxsz) +#if defined(CONFIG_NFS_V4_1) +#define NFS4_enc_exchange_id_sz \ + (compound_encode_hdr_maxsz + \ + encode_exchange_id_maxsz) +#define NFS4_dec_exchange_id_sz \ + (compound_decode_hdr_maxsz + \ + decode_exchange_id_maxsz) +#endif /* CONFIG_NFS_V4_1 */ static const umode_t nfs_type2fmt[] = { [NF4BAD] = 0, @@ -1455,7 +1484,29 @@ static void encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *state hdr->replen += decode_delegreturn_maxsz; } +#if defined(CONFIG_NFS_V4_1) /* NFSv4.1 operations */ +static void encode_exchange_id(struct xdr_stream *xdr, + struct nfs41_exchange_id_args *args, + struct compound_hdr *hdr) +{ + __be32 *p; + + RESERVE_SPACE(4 + sizeof(args->verifier->data)); + WRITE32(OP_EXCHANGE_ID); + WRITEMEM(args->verifier->data, sizeof(args->verifier->data)); + + encode_string(xdr, args->id_len, args->id); + + RESERVE_SPACE(12); + WRITE32(args->flags); + WRITE32(0); /* zero length state_protect4_a */ + WRITE32(0); /* zero length implementation id array */ + hdr->nops++; + hdr->replen += decode_exchange_id_maxsz; +} +#endif /* CONFIG_NFS_V4_1 */ + static void encode_sequence(struct xdr_stream *xdr, const struct nfs4_sequence_args *args, struct compound_hdr *hdr) @@ -2162,6 +2213,26 @@ static int nfs4_xdr_enc_fs_locations(struct rpc_rqst *req, __be32 *p, struct nfs return 0; } +#if defined(CONFIG_NFS_V4_1) +/* + * EXCHANGE_ID request + */ +static int nfs4_xdr_enc_exchange_id(struct rpc_rqst *req, uint32_t *p, + struct nfs41_exchange_id_args *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .minorversion = args->client->cl_minorversion, + }; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, req, &hdr); + encode_exchange_id(&xdr, args, &hdr); + encode_nops(&hdr); + return 0; +} +#endif /* CONFIG_NFS_V4_1 */ + /* * START OF "GENERIC" DECODE ROUTINES. * These may look a little ugly since they are imported from a "generic" @@ -3877,6 +3948,52 @@ static int decode_delegreturn(struct xdr_stream *xdr) return decode_op_hdr(xdr, OP_DELEGRETURN); } +#if defined(CONFIG_NFS_V4_1) +static int decode_exchange_id(struct xdr_stream *xdr, + struct nfs41_exchange_id_res *res) +{ + __be32 *p; + uint32_t dummy; + int status; + struct nfs_client *clp = res->client; + + status = decode_op_hdr(xdr, OP_EXCHANGE_ID); + if (status) + return status; + + READ_BUF(8); + READ64(clp->cl_ex_clid); + READ_BUF(12); + READ32(clp->cl_seqid); + READ32(clp->cl_exchange_flags); + + /* We ask for SP4_NONE */ + READ32(dummy); + if (dummy != SP4_NONE) + return -EIO; + + /* Throw away minor_id */ + READ_BUF(8); + + /* Throw away Major id */ + READ_BUF(4); + READ32(dummy); + READ_BUF(dummy); + + /* Throw away server_scope */ + READ_BUF(4); + READ32(dummy); + READ_BUF(dummy); + + /* Throw away Implementation id array */ + READ_BUF(4); + READ32(dummy); + READ_BUF(dummy); + + return 0; +} +#endif /* CONFIG_NFS_V4_1 */ + static int decode_sequence(struct xdr_stream *xdr, struct nfs4_sequence_res *res, struct rpc_rqst *rqstp) @@ -4774,6 +4891,25 @@ out: return status; } +#if defined(CONFIG_NFS_V4_1) +/* + * EXCHANGE_ID request + */ +static int nfs4_xdr_dec_exchange_id(struct rpc_rqst *rqstp, uint32_t *p, + void *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (!status) + status = decode_exchange_id(&xdr, res); + return status; +} +#endif /* CONFIG_NFS_V4_1 */ + __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) { uint32_t bitmap[2] = {0}; @@ -4943,6 +5079,9 @@ struct rpc_procinfo nfs4_procedures[] = { PROC(GETACL, enc_getacl, dec_getacl), PROC(SETACL, enc_setacl, dec_setacl), PROC(FS_LOCATIONS, enc_fs_locations, dec_fs_locations), +#if defined(CONFIG_NFS_V4_1) + PROC(EXCHANGE_ID, enc_exchange_id, dec_exchange_id), +#endif /* CONFIG_NFS_V4_1 */ }; struct rpc_version nfs_version4 = { diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index 7c36fcf2dfb..ad65709ed8d 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -21,6 +21,7 @@ #define NFS4_FHSIZE 128 #define NFS4_MAXPATHLEN PATH_MAX #define NFS4_MAXNAMLEN NAME_MAX +#define NFS4_OPAQUE_LIMIT 1024 #define NFS4_MAX_SESSIONID_LEN 16 #define NFS4_ACCESS_READ 0x0001 diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 206485e5082..435ed556efb 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -78,6 +78,12 @@ struct nfs_client { #endif /* CONFIG_NFS_V4 */ #ifdef CONFIG_NFS_V4_1 + /* clientid returned from EXCHANGE_ID, used by session operations */ + u64 cl_ex_clid; + /* The sequence id to use for the next CREATE_SESSION */ + u32 cl_seqid; + /* The flags used for obtaining the clientid during EXCHANGE_ID */ + u32 cl_exchange_flags; struct nfs4_session *cl_session; /* sharred session */ #endif /* CONFIG_NFS_V4_1 */ diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 4ac14b40efc..5d70b924af5 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -879,6 +879,46 @@ struct nfs4_fs_locations_res { #endif /* CONFIG_NFS_V4 */ +struct nfstime4 { + u64 seconds; + u32 nseconds; +}; + +#ifdef CONFIG_NFS_V4_1 +struct nfs_impl_id4 { + u32 domain_len; + char *domain; + u32 name_len; + char *name; + struct nfstime4 date; +}; + +#define NFS4_EXCHANGE_ID_LEN (48) +struct nfs41_exchange_id_args { + struct nfs_client *client; + nfs4_verifier *verifier; + unsigned int id_len; + char id[NFS4_EXCHANGE_ID_LEN]; + u32 flags; +}; + +struct server_owner { + uint64_t minor_id; + uint32_t major_id_sz; + char major_id[NFS4_OPAQUE_LIMIT]; +}; + +struct server_scope { + uint32_t server_scope_sz; + char server_scope[NFS4_OPAQUE_LIMIT]; +}; + +struct nfs41_exchange_id_res { + struct nfs_client *client; + u32 flags; +}; +#endif /* CONFIG_NFS_V4_1 */ + struct nfs_page; #define NFS_PAGEVEC_SIZE (8U) diff --git a/include/linux/nfsd/state.h b/include/linux/nfsd/state.h index 4d61c873fee..7ef4b7ad121 100644 --- a/include/linux/nfsd/state.h +++ b/include/linux/nfsd/state.h @@ -41,7 +41,6 @@ #include #include -#define NFS4_OPAQUE_LIMIT 1024 typedef struct { u32 cl_boot; u32 cl_id; -- cgit v1.2.3 From 2050f0cc0703aab7cee798b3cb47037754f368bc Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:30 -0400 Subject: nfs41: get_lease_time get_lease_time uses the FSINFO rpc operation to get the lease time attribute. nfs4_get_lease_time() is only called from the state manager on session setup so don't recover from clientid or sequence level errors. We do need to recover from NFS4ERR_DELAY or NFS4ERR_GRACE. Use NFS4_POLL_RETRY_MIN - the Linux server returns NFS4ERR_DELAY when an upcall is needed to resolve an uncached export referenced by a file handle. [nfs41: sequence res use slotid] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: remove extraneous rpc_clnt pointer] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: have get_lease_time work on nfs_client] Signed-off-by: Benny Halevy [nfs41: get_lease_time recover from NFS4ERR_DELAY] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: pass *session in seq_args and seq_res] [define nfs4_get_lease_time_{args,res}] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/nfs/nfs4xdr.c | 51 +++++++++++++++++++++++++++ include/linux/nfs_xdr.h | 9 +++++ 3 files changed, 154 insertions(+) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 6f384e29075..eafc99afd35 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4189,6 +4189,100 @@ static int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred) return status; } +struct nfs4_get_lease_time_data { + struct nfs4_get_lease_time_args *args; + struct nfs4_get_lease_time_res *res; + struct nfs_client *clp; +}; + +static void nfs4_get_lease_time_prepare(struct rpc_task *task, + void *calldata) +{ + int ret; + struct nfs4_get_lease_time_data *data = + (struct nfs4_get_lease_time_data *)calldata; + + dprintk("--> %s\n", __func__); + /* just setup sequence, do not trigger session recovery + since we're invoked within one */ + ret = nfs41_setup_sequence(data->clp->cl_session, + &data->args->la_seq_args, + &data->res->lr_seq_res, 0, task); + + BUG_ON(ret == -EAGAIN); + rpc_call_start(task); + dprintk("<-- %s\n", __func__); +} + +/* + * Called from nfs4_state_manager thread for session setup, so don't recover + * from sequence operation or clientid errors. + */ +static void nfs4_get_lease_time_done(struct rpc_task *task, void *calldata) +{ + struct nfs4_get_lease_time_data *data = + (struct nfs4_get_lease_time_data *)calldata; + + dprintk("--> %s\n", __func__); + nfs41_sequence_done(data->clp, &data->res->lr_seq_res, task->tk_status); + switch (task->tk_status) { + case -NFS4ERR_DELAY: + case -NFS4ERR_GRACE: + dprintk("%s Retry: tk_status %d\n", __func__, task->tk_status); + rpc_delay(task, NFS4_POLL_RETRY_MIN); + task->tk_status = 0; + rpc_restart_call(task); + return; + } + nfs41_sequence_free_slot(data->clp, &data->res->lr_seq_res); + dprintk("<-- %s\n", __func__); +} + +struct rpc_call_ops nfs4_get_lease_time_ops = { + .rpc_call_prepare = nfs4_get_lease_time_prepare, + .rpc_call_done = nfs4_get_lease_time_done, +}; + +int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo) +{ + struct rpc_task *task; + struct nfs4_get_lease_time_args args; + struct nfs4_get_lease_time_res res = { + .lr_fsinfo = fsinfo, + }; + struct nfs4_get_lease_time_data data = { + .args = &args, + .res = &res, + .clp = clp, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GET_LEASE_TIME], + .rpc_argp = &args, + .rpc_resp = &res, + }; + struct rpc_task_setup task_setup = { + .rpc_client = clp->cl_rpcclient, + .rpc_message = &msg, + .callback_ops = &nfs4_get_lease_time_ops, + .callback_data = &data + }; + int status; + + res.lr_seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; + dprintk("--> %s\n", __func__); + task = rpc_run_task(&task_setup); + + if (IS_ERR(task)) + status = PTR_ERR(task); + else { + status = task->tk_status; + rpc_put_task(task); + } + dprintk("<-- %s return %d\n", __func__, status); + + return status; +} + /* Destroy the slot table */ static void nfs4_destroy_slot_table(struct nfs4_session *session) { diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 783c4214dcc..85ee1d17a46 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -622,6 +622,14 @@ static int nfs4_stat_to_errno(int); #define NFS4_dec_exchange_id_sz \ (compound_decode_hdr_maxsz + \ decode_exchange_id_maxsz) +#define NFS4_enc_get_lease_time_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ + encode_putrootfh_maxsz + \ + encode_fsinfo_maxsz) +#define NFS4_dec_get_lease_time_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ + decode_putrootfh_maxsz + \ + decode_fsinfo_maxsz) #endif /* CONFIG_NFS_V4_1 */ static const umode_t nfs_type2fmt[] = { @@ -2231,6 +2239,27 @@ static int nfs4_xdr_enc_exchange_id(struct rpc_rqst *req, uint32_t *p, encode_nops(&hdr); return 0; } + +/* + * a GET_LEASE_TIME request + */ +static int nfs4_xdr_enc_get_lease_time(struct rpc_rqst *req, uint32_t *p, + struct nfs4_get_lease_time_args *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .minorversion = nfs4_xdr_minorversion(&args->la_seq_args), + }; + const u32 lease_bitmap[2] = { FATTR4_WORD0_LEASE_TIME, 0 }; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, &args->la_seq_args, &hdr); + encode_putrootfh(&xdr, &hdr); + encode_fsinfo(&xdr, lease_bitmap, &hdr); + encode_nops(&hdr); + return 0; +} #endif /* CONFIG_NFS_V4_1 */ /* @@ -4908,6 +4937,27 @@ static int nfs4_xdr_dec_exchange_id(struct rpc_rqst *rqstp, uint32_t *p, status = decode_exchange_id(&xdr, res); return status; } + +/* + * a GET_LEASE_TIME request + */ +static int nfs4_xdr_dec_get_lease_time(struct rpc_rqst *rqstp, uint32_t *p, + struct nfs4_get_lease_time_res *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (!status) + status = decode_sequence(&xdr, &res->lr_seq_res, rqstp); + if (!status) + status = decode_putrootfh(&xdr); + if (!status) + status = decode_fsinfo(&xdr, res->lr_fsinfo); + return status; +} #endif /* CONFIG_NFS_V4_1 */ __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) @@ -5081,6 +5131,7 @@ struct rpc_procinfo nfs4_procedures[] = { PROC(FS_LOCATIONS, enc_fs_locations, dec_fs_locations), #if defined(CONFIG_NFS_V4_1) PROC(EXCHANGE_ID, enc_exchange_id, dec_exchange_id), + PROC(GET_LEASE_TIME, enc_get_lease_time, dec_get_lease_time), #endif /* CONFIG_NFS_V4_1 */ }; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 5d70b924af5..ca643aa87d4 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -174,6 +174,15 @@ struct nfs4_sequence_res { int sr_status; /* sequence operation status */ }; +struct nfs4_get_lease_time_args { + struct nfs4_sequence_args la_seq_args; +}; + +struct nfs4_get_lease_time_res { + struct nfs_fsinfo *lr_fsinfo; + struct nfs4_sequence_res lr_seq_res; +}; + /* * Arguments to the open call. */ -- cgit v1.2.3 From fc931582c260e53ca5ca23bd70ccc9b2265cca9f Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:31 -0400 Subject: nfs41: create_session operation Implement the create_session operation conforming to http://tools.ietf.org/html/draft-ietf-nfsv4-minorversion1-26 Set the real fore channel max operations to preserve server resources. Note: If the server returns < NFS4_MAX_OPS, the client will very soon get an NFS4ERR_TOO_MANY_OPS. A later patch will handle this. Set the max_rqst_sz and max_resp_sz to PAGE_SIZE - we preallocate the buffers. Set the back channel max_resp_sz_cached to zero to force the client to always set csa_cachethis to FALSE because the current implementation of the back channel DRC only supports caching the CB_SEQUENCE operation. The client back channel server supports one slot, and desires 2 operations per compound. Signed-off-by: Ricardo Labiaga Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: remove extraneous rpc_clnt pointer] Use the struct nfs_client cl_rpcclient. Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: nfs4_init_channel_attrs, just use nfs41_create_session_args] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: use rsize and wsize for session channel attributes] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: set channel max operations] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: set back channel attributes] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: obliterate nfs4_adjust_channel_attrs] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: have create_session work on nfs_client] Signed-off-by: Benny Halevy [nfs41: move CONFIG_NFS_V4_1 endif] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: pass *session in seq_args and seq_res] [moved nfs4_init_slot_table definition here] Signed-off-by: Benny Halevy [nfs41: use kcalloc to allocate slot table] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust [nfs41: fix Xcode_create_session's xdr Xcoding pointer type] [nfs41: refactor decoding of channel attributes] Signed-off-by: Benny Halevy --- fs/nfs/nfs4proc.c | 172 ++++++++++++++++++++++++++++++++++++++++++++ fs/nfs/nfs4xdr.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/nfs4.h | 10 +++ include/linux/nfs_xdr.h | 12 ++++ 4 files changed, 379 insertions(+) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index eafc99afd35..7d81d6e5753 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -54,6 +54,7 @@ #include "delegation.h" #include "internal.h" #include "iostat.h" +#include "callback.h" #define NFSDBG_FACILITY NFSDBG_PROC @@ -4283,6 +4284,50 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo) return status; } +/* + * Initialize slot table + */ +static int nfs4_init_slot_table(struct nfs4_session *session) +{ + struct nfs4_slot_table *tbl = &session->fc_slot_table; + int i, max_slots = session->fc_attrs.max_reqs; + struct nfs4_slot *slot; + int ret = -ENOMEM; + + BUG_ON(max_slots > NFS4_MAX_SLOT_TABLE); + + dprintk("--> %s: max_reqs=%u\n", __func__, + session->fc_attrs.max_reqs); + + slot = kcalloc(max_slots, sizeof(struct nfs4_slot), GFP_KERNEL); + if (!slot) + goto out; + for (i = 0; i < max_slots; ++i) + slot[i].seq_nr = 1; + ret = 0; + + spin_lock(&tbl->slot_tbl_lock); + if (tbl->slots != NULL) { + spin_unlock(&tbl->slot_tbl_lock); + dprintk("%s: slot table already initialized. tbl=%p slots=%p\n", + __func__, tbl, tbl->slots); + WARN_ON(1); + goto out_free; + } + tbl->max_slots = max_slots; + tbl->slots = slot; + tbl->highest_used_slotid = -1; /* no slot is currently used */ + spin_unlock(&tbl->slot_tbl_lock); + dprintk("%s: tbl=%p slots=%p max_slots=%d\n", __func__, + tbl, tbl->slots, tbl->max_slots); +out: + dprintk("<-- %s: return %d\n", __func__, ret); + return ret; +out_free: + kfree(slot); + goto out; +} + /* Destroy the slot table */ static void nfs4_destroy_slot_table(struct nfs4_session *session) { @@ -4314,6 +4359,133 @@ void nfs4_destroy_session(struct nfs4_session *session) kfree(session); } +/* + * Initialize the values to be used by the client in CREATE_SESSION + * If nfs4_init_session set the fore channel request and response sizes, + * use them. + * + * Set the back channel max_resp_sz_cached to zero to force the client to + * always set csa_cachethis to FALSE because the current implementation + * of the back channel DRC only supports caching the CB_SEQUENCE operation. + */ +static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args) +{ + struct nfs4_session *session = args->client->cl_session; + unsigned int mxrqst_sz = session->fc_attrs.max_rqst_sz, + mxresp_sz = session->fc_attrs.max_resp_sz; + + if (mxrqst_sz == 0) + mxrqst_sz = NFS_MAX_FILE_IO_SIZE; + if (mxresp_sz == 0) + mxresp_sz = NFS_MAX_FILE_IO_SIZE; + /* Fore channel attributes */ + args->fc_attrs.headerpadsz = 0; + args->fc_attrs.max_rqst_sz = mxrqst_sz; + args->fc_attrs.max_resp_sz = mxresp_sz; + args->fc_attrs.max_resp_sz_cached = mxresp_sz; + args->fc_attrs.max_ops = NFS4_MAX_OPS; + args->fc_attrs.max_reqs = session->clp->cl_rpcclient->cl_xprt->max_reqs; + + dprintk("%s: Fore Channel : max_rqst_sz=%u max_resp_sz=%u " + "max_resp_sz_cached=%u max_ops=%u max_reqs=%u\n", + __func__, + args->fc_attrs.max_rqst_sz, args->fc_attrs.max_resp_sz, + args->fc_attrs.max_resp_sz_cached, args->fc_attrs.max_ops, + args->fc_attrs.max_reqs); + + /* Back channel attributes */ + args->bc_attrs.headerpadsz = 0; + args->bc_attrs.max_rqst_sz = PAGE_SIZE; + args->bc_attrs.max_resp_sz = PAGE_SIZE; + args->bc_attrs.max_resp_sz_cached = 0; + args->bc_attrs.max_ops = NFS4_MAX_BACK_CHANNEL_OPS; + args->bc_attrs.max_reqs = 1; + + dprintk("%s: Back Channel : max_rqst_sz=%u max_resp_sz=%u " + "max_resp_sz_cached=%u max_ops=%u max_reqs=%u\n", + __func__, + args->bc_attrs.max_rqst_sz, args->bc_attrs.max_resp_sz, + args->bc_attrs.max_resp_sz_cached, args->bc_attrs.max_ops, + args->bc_attrs.max_reqs); +} + +static int _nfs4_proc_create_session(struct nfs_client *clp) +{ + struct nfs4_session *session = clp->cl_session; + struct nfs41_create_session_args args = { + .client = clp, + .cb_program = NFS4_CALLBACK, + }; + struct nfs41_create_session_res res = { + .client = clp, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE_SESSION], + .rpc_argp = &args, + .rpc_resp = &res, + }; + int status; + + nfs4_init_channel_attrs(&args); + args.flags = (SESSION4_PERSIST); + + status = rpc_call_sync(session->clp->cl_rpcclient, &msg, 0); + + /* Set the negotiated values in the session's channel_attrs struct */ + + if (!status) { + /* Increment the clientid slot sequence id */ + clp->cl_seqid++; + } + + return status; +} + +/* + * Issues a CREATE_SESSION operation to the server. + * It is the responsibility of the caller to verify the session is + * expired before calling this routine. + */ +int nfs4_proc_create_session(struct nfs_client *clp) +{ + int status; + unsigned *ptr; + struct nfs_fsinfo fsinfo; + struct nfs4_session *session = clp->cl_session; + + dprintk("--> %s clp=%p session=%p\n", __func__, clp, session); + + status = _nfs4_proc_create_session(clp); + if (status) + goto out; + + /* Init the fore channel */ + status = nfs4_init_slot_table(session); + dprintk("fore channel slot table initialization returned %d\n", status); + if (status) + goto out; + + ptr = (unsigned *)&session->sess_id.data[0]; + dprintk("%s client>seqid %d sessionid %u:%u:%u:%u\n", __func__, + clp->cl_seqid, ptr[0], ptr[1], ptr[2], ptr[3]); + + /* Get the lease time */ + status = nfs4_proc_get_lease_time(clp, &fsinfo); + if (status == 0) { + /* Update lease time and schedule renewal */ + spin_lock(&clp->cl_lock); + clp->cl_lease_time = fsinfo.lease_time * HZ; + clp->cl_last_renewal = jiffies; + clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); + spin_unlock(&clp->cl_lock); + + nfs4_schedule_state_renewal(clp); + } +out: + dprintk("<-- %s\n", __func__); + return status; +} + #endif /* CONFIG_NFS_V4_1 */ struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops = { diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 85ee1d17a46..7a243a2cf0b 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -246,6 +246,8 @@ static int nfs4_stat_to_errno(int); (0) #if defined(CONFIG_NFS_V4_1) +#define NFS4_MAX_MACHINE_NAME_LEN (64) + #define encode_exchange_id_maxsz (op_encode_hdr_maxsz + \ encode_verifier_maxsz + \ 1 /* co_ownerid.len */ + \ @@ -267,6 +269,31 @@ static int nfs4_stat_to_errno(int); XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1 + \ 1 /* eir_server_impl_id array length */ + \ 0 /* ignored eir_server_impl_id contents */) +#define encode_channel_attrs_maxsz (6 + 1 /* ca_rdma_ird.len (0) */) +#define decode_channel_attrs_maxsz (6 + \ + 1 /* ca_rdma_ird.len */ + \ + 1 /* ca_rdma_ird */) +#define encode_create_session_maxsz (op_encode_hdr_maxsz + \ + 2 /* csa_clientid */ + \ + 1 /* csa_sequence */ + \ + 1 /* csa_flags */ + \ + encode_channel_attrs_maxsz + \ + encode_channel_attrs_maxsz + \ + 1 /* csa_cb_program */ + \ + 1 /* csa_sec_parms.len (1) */ + \ + 1 /* cb_secflavor (AUTH_SYS) */ + \ + 1 /* stamp */ + \ + 1 /* machinename.len */ + \ + XDR_QUADLEN(NFS4_MAX_MACHINE_NAME_LEN) + \ + 1 /* uid */ + \ + 1 /* gid */ + \ + 1 /* gids.len (0) */) +#define decode_create_session_maxsz (op_decode_hdr_maxsz + \ + XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + \ + 1 /* csr_sequence */ + \ + 1 /* csr_flags */ + \ + decode_channel_attrs_maxsz + \ + decode_channel_attrs_maxsz) #define encode_sequence_maxsz 0 /* stub */ #define decode_sequence_maxsz 0 /* stub */ #else /* CONFIG_NFS_V4_1 */ @@ -622,6 +649,12 @@ static int nfs4_stat_to_errno(int); #define NFS4_dec_exchange_id_sz \ (compound_decode_hdr_maxsz + \ decode_exchange_id_maxsz) +#define NFS4_enc_create_session_sz \ + (compound_encode_hdr_maxsz + \ + encode_create_session_maxsz) +#define NFS4_dec_create_session_sz \ + (compound_decode_hdr_maxsz + \ + decode_create_session_maxsz) #define NFS4_enc_get_lease_time_sz (compound_encode_hdr_maxsz + \ encode_sequence_maxsz + \ encode_putrootfh_maxsz + \ @@ -712,6 +745,7 @@ static void encode_compound_hdr(struct xdr_stream *xdr, static void encode_nops(struct compound_hdr *hdr) { + BUG_ON(hdr->nops > NFS4_MAX_OPS); *hdr->nops_p = htonl(hdr->nops); } @@ -1513,6 +1547,68 @@ static void encode_exchange_id(struct xdr_stream *xdr, hdr->nops++; hdr->replen += decode_exchange_id_maxsz; } + +static void encode_create_session(struct xdr_stream *xdr, + struct nfs41_create_session_args *args, + struct compound_hdr *hdr) +{ + __be32 *p; + char machine_name[NFS4_MAX_MACHINE_NAME_LEN]; + uint32_t len; + struct nfs_client *clp = args->client; + + RESERVE_SPACE(4); + WRITE32(OP_CREATE_SESSION); + + RESERVE_SPACE(8); + WRITE64(clp->cl_ex_clid); + + RESERVE_SPACE(8); + WRITE32(clp->cl_seqid); /*Sequence id */ + WRITE32(args->flags); /*flags */ + + RESERVE_SPACE(2*28); /* 2 channel_attrs */ + /* Fore Channel */ + WRITE32(args->fc_attrs.headerpadsz); /* header padding size */ + WRITE32(args->fc_attrs.max_rqst_sz); /* max req size */ + WRITE32(args->fc_attrs.max_resp_sz); /* max resp size */ + WRITE32(args->fc_attrs.max_resp_sz_cached); /* Max resp sz cached */ + WRITE32(args->fc_attrs.max_ops); /* max operations */ + WRITE32(args->fc_attrs.max_reqs); /* max requests */ + WRITE32(0); /* rdmachannel_attrs */ + + /* Back Channel */ + WRITE32(args->fc_attrs.headerpadsz); /* header padding size */ + WRITE32(args->bc_attrs.max_rqst_sz); /* max req size */ + WRITE32(args->bc_attrs.max_resp_sz); /* max resp size */ + WRITE32(args->bc_attrs.max_resp_sz_cached); /* Max resp sz cached */ + WRITE32(args->bc_attrs.max_ops); /* max operations */ + WRITE32(args->bc_attrs.max_reqs); /* max requests */ + WRITE32(0); /* rdmachannel_attrs */ + + RESERVE_SPACE(4); + WRITE32(args->cb_program); /* cb_program */ + + RESERVE_SPACE(4); /* # of security flavors */ + WRITE32(1); + + RESERVE_SPACE(4); + WRITE32(RPC_AUTH_UNIX); /* auth_sys */ + + /* authsys_parms rfc1831 */ + RESERVE_SPACE(4); + WRITE32((u32)clp->cl_boot_time.tv_nsec); /* stamp */ + len = scnprintf(machine_name, sizeof(machine_name), "%s", + clp->cl_ipaddr); + RESERVE_SPACE(16 + len); + WRITE32(len); + WRITEMEM(machine_name, len); + WRITE32(0); /* UID */ + WRITE32(0); /* GID */ + WRITE32(0); /* No more gids */ + hdr->nops++; + hdr->replen += decode_create_session_maxsz; +} #endif /* CONFIG_NFS_V4_1 */ static void encode_sequence(struct xdr_stream *xdr, @@ -2240,6 +2336,24 @@ static int nfs4_xdr_enc_exchange_id(struct rpc_rqst *req, uint32_t *p, return 0; } +/* + * a CREATE_SESSION request + */ +static int nfs4_xdr_enc_create_session(struct rpc_rqst *req, uint32_t *p, + struct nfs41_create_session_args *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .minorversion = args->client->cl_minorversion, + }; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, req, &hdr); + encode_create_session(&xdr, args, &hdr); + encode_nops(&hdr); + return 0; +} + /* * a GET_LEASE_TIME request */ @@ -4021,6 +4135,59 @@ static int decode_exchange_id(struct xdr_stream *xdr, return 0; } + +static int decode_chan_attrs(struct xdr_stream *xdr, + struct nfs4_channel_attrs *attrs) +{ + __be32 *p; + u32 nr_attrs; + + READ_BUF(28); + READ32(attrs->headerpadsz); + READ32(attrs->max_rqst_sz); + READ32(attrs->max_resp_sz); + READ32(attrs->max_resp_sz_cached); + READ32(attrs->max_ops); + READ32(attrs->max_reqs); + READ32(nr_attrs); + if (unlikely(nr_attrs > 1)) { + printk(KERN_WARNING "%s: Invalid rdma channel attrs count %u\n", + __func__, nr_attrs); + return -EINVAL; + } + if (nr_attrs == 1) + READ_BUF(4); /* skip rdma_attrs */ + return 0; +} + +static int decode_create_session(struct xdr_stream *xdr, + struct nfs41_create_session_res *res) +{ + __be32 *p; + int status; + struct nfs_client *clp = res->client; + struct nfs4_session *session = clp->cl_session; + + status = decode_op_hdr(xdr, OP_CREATE_SESSION); + + if (status) + return status; + + /* sessionid */ + READ_BUF(NFS4_MAX_SESSIONID_LEN); + COPYMEM(&session->sess_id, NFS4_MAX_SESSIONID_LEN); + + /* seqid, flags */ + READ_BUF(8); + READ32(clp->cl_seqid); + READ32(session->flags); + + /* Channel attributes */ + status = decode_chan_attrs(xdr, &session->fc_attrs); + if (!status) + status = decode_chan_attrs(xdr, &session->bc_attrs); + return status; +} #endif /* CONFIG_NFS_V4_1 */ static int decode_sequence(struct xdr_stream *xdr, @@ -4938,6 +5105,23 @@ static int nfs4_xdr_dec_exchange_id(struct rpc_rqst *rqstp, uint32_t *p, return status; } +/* + * a CREATE_SESSION request + */ +static int nfs4_xdr_dec_create_session(struct rpc_rqst *rqstp, uint32_t *p, + struct nfs41_create_session_res *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (!status) + status = decode_create_session(&xdr, res); + return status; +} + /* * a GET_LEASE_TIME request */ @@ -5131,6 +5315,7 @@ struct rpc_procinfo nfs4_procedures[] = { PROC(FS_LOCATIONS, enc_fs_locations, dec_fs_locations), #if defined(CONFIG_NFS_V4_1) PROC(EXCHANGE_ID, enc_exchange_id, dec_exchange_id), + PROC(CREATE_SESSION, enc_create_session, dec_create_session), PROC(GET_LEASE_TIME, enc_get_lease_time, dec_get_lease_time), #endif /* CONFIG_NFS_V4_1 */ }; diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index ad65709ed8d..bd2eba53066 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -131,6 +131,16 @@ #define NFS4_MAX_UINT64 (~(u64)0) +/* An NFS4 sessions server must support at least NFS4_MAX_OPS operations. + * If a compound requires more operations, adjust NFS4_MAX_OPS accordingly. + */ +#define NFS4_MAX_OPS 8 + +/* Our NFS4 client back channel server only wants the cb_sequene and the + * actual operation per compound + */ +#define NFS4_MAX_BACK_CHANNEL_OPS 2 + enum nfs4_acl_whotype { NFS4_ACL_WHO_NAMED = 0, NFS4_ACL_WHO_OWNER, diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index ca643aa87d4..62f63fb0c4c 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -926,6 +926,18 @@ struct nfs41_exchange_id_res { struct nfs_client *client; u32 flags; }; + +struct nfs41_create_session_args { + struct nfs_client *client; + uint32_t flags; + uint32_t cb_program; + struct nfs4_channel_attrs fc_attrs; /* Fore Channel */ + struct nfs4_channel_attrs bc_attrs; /* Back Channel */ +}; + +struct nfs41_create_session_res { + struct nfs_client *client; +}; #endif /* CONFIG_NFS_V4_1 */ struct nfs_page; -- cgit v1.2.3 From 8d35301d7db4165f2430c428865f7b0add47615d Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:32 -0400 Subject: nfs41: verify session channel attribues Invalidate the session if the server returns invalid fore or back channel attributes. Use a KERN_WARNING to report the fatal session estabishment error. Signed-off-by: Andy Adamson [refactor nfs4_verify_channel_attrs] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 7d81d6e5753..4d7a8b9e129 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4409,6 +4409,51 @@ static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args) args->bc_attrs.max_reqs); } +static int _verify_channel_attr(char *chan, char *attr_name, u32 sent, u32 rcvd) +{ + if (rcvd <= sent) + return 0; + printk(KERN_WARNING "%s: Session INVALID: %s channel %s increased. " + "sent=%u rcvd=%u\n", __func__, chan, attr_name, sent, rcvd); + return -EINVAL; +} + +#define _verify_fore_channel_attr(_name_) \ + _verify_channel_attr("fore", #_name_, \ + args->fc_attrs._name_, \ + session->fc_attrs._name_) + +#define _verify_back_channel_attr(_name_) \ + _verify_channel_attr("back", #_name_, \ + args->bc_attrs._name_, \ + session->bc_attrs._name_) + +/* + * The server is not allowed to increase the fore channel header pad size, + * maximum response size, or maximum number of operations. + * + * The back channel attributes are only negotiatied down: We send what the + * (back channel) server insists upon. + */ +static int nfs4_verify_channel_attrs(struct nfs41_create_session_args *args, + struct nfs4_session *session) +{ + int ret = 0; + + ret |= _verify_fore_channel_attr(headerpadsz); + ret |= _verify_fore_channel_attr(max_resp_sz); + ret |= _verify_fore_channel_attr(max_ops); + + ret |= _verify_back_channel_attr(headerpadsz); + ret |= _verify_back_channel_attr(max_rqst_sz); + ret |= _verify_back_channel_attr(max_resp_sz); + ret |= _verify_back_channel_attr(max_resp_sz_cached); + ret |= _verify_back_channel_attr(max_ops); + ret |= _verify_back_channel_attr(max_reqs); + + return ret; +} + static int _nfs4_proc_create_session(struct nfs_client *clp) { struct nfs4_session *session = clp->cl_session; @@ -4431,8 +4476,9 @@ static int _nfs4_proc_create_session(struct nfs_client *clp) status = rpc_call_sync(session->clp->cl_rpcclient, &msg, 0); - /* Set the negotiated values in the session's channel_attrs struct */ - + if (!status) + /* Verify the session's negotiated channel_attrs values */ + status = nfs4_verify_channel_attrs(&args, session); if (!status) { /* Increment the clientid slot sequence id */ clp->cl_seqid++; -- cgit v1.2.3 From 96b09e024fedf0a6604c8c688a3994d5ed991434 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:33 -0400 Subject: nfs41: use session attributes for rsize and wsize Set the mount points rsize and wsize to the negotiated session fore channel maximum response and requeset size. These values will be bound checked in nfs_server_set_fsinfo. Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [move nfs4_session_set_rwsize into CONFIG_NFS_V4] Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index a9828baaa44..bb7432d83b5 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1206,6 +1206,21 @@ static void nfs4_init_session(struct nfs_client *clp, #endif /* CONFIG_NFS_V4_1 */ } +/* + * Session has been established, and the client marked ready. + * Set the mount rsize and wsize with negotiated fore channel + * attributes which will be bound checked in nfs_server_set_fsinfo. + */ +static void nfs4_session_set_rwsize(struct nfs_server *server) +{ +#ifdef CONFIG_NFS_V4_1 + if (!nfs4_has_session(server->nfs_client)) + return; + server->rsize = server->nfs_client->cl_session->fc_attrs.max_resp_sz; + server->wsize = server->nfs_client->cl_session->fc_attrs.max_rqst_sz; +#endif /* CONFIG_NFS_V4_1 */ +} + /* * Create a version 4 volume record */ @@ -1296,6 +1311,8 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, (unsigned long long) server->fsid.minor); dprintk("Mount FH: %d\n", mntfh->size); + nfs4_session_set_rwsize(server); + error = nfs_probe_fsinfo(server, mntfh, &fattr); if (error < 0) goto error; -- cgit v1.2.3 From 0f3e66c6a6cae479028d31198288e524fe5ff90d Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:34 -0400 Subject: nfs41: destroy_session operation Implement the destroy_session operation conforming to http://tools.ietf.org/html/draft-ietf-nfsv4-minorversion1-26 Signed-off-by: Ricardo Labiaga Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: remove extraneous rpc_clnt pointer] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41; NFS_CS_READY required for DESTROY_SESSION] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: pass *session in seq_args and seq_res] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust [nfs41: fix encode_destroy_session's xdr Xcoding pointer type] Signed-off-by: Benny Halevy --- fs/nfs/nfs4proc.c | 30 ++++++++++++++++++++++++++++ fs/nfs/nfs4xdr.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/nfs/super.c | 2 ++ 3 files changed, 91 insertions(+) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 4d7a8b9e129..59e8bf5b10b 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4532,6 +4532,36 @@ out: return status; } +/* + * Issue the over-the-wire RPC DESTROY_SESSION. + * The caller must serialize access to this routine. + */ +int nfs4_proc_destroy_session(struct nfs4_session *session) +{ + int status = 0; + struct rpc_message msg; + + dprintk("--> nfs4_proc_destroy_session\n"); + + /* session is still being setup */ + if (session->clp->cl_cons_state != NFS_CS_READY) + return status; + + msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DESTROY_SESSION]; + msg.rpc_argp = session; + msg.rpc_resp = NULL; + msg.rpc_cred = NULL; + status = rpc_call_sync(session->clp->cl_rpcclient, &msg, 0); + + if (status) + printk(KERN_WARNING + "Got error %d from the server on DESTROY_SESSION. " + "Session has been destroyed regardless...\n", status); + + dprintk("<-- nfs4_proc_destroy_session\n"); + return status; +} + #endif /* CONFIG_NFS_V4_1 */ struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops = { diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 7a243a2cf0b..a81e49648cc 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -294,6 +294,8 @@ static int nfs4_stat_to_errno(int); 1 /* csr_flags */ + \ decode_channel_attrs_maxsz + \ decode_channel_attrs_maxsz) +#define encode_destroy_session_maxsz (op_encode_hdr_maxsz + 4) +#define decode_destroy_session_maxsz (op_decode_hdr_maxsz) #define encode_sequence_maxsz 0 /* stub */ #define decode_sequence_maxsz 0 /* stub */ #else /* CONFIG_NFS_V4_1 */ @@ -655,6 +657,10 @@ static int nfs4_stat_to_errno(int); #define NFS4_dec_create_session_sz \ (compound_decode_hdr_maxsz + \ decode_create_session_maxsz) +#define NFS4_enc_destroy_session_sz (compound_encode_hdr_maxsz + \ + encode_destroy_session_maxsz) +#define NFS4_dec_destroy_session_sz (compound_decode_hdr_maxsz + \ + decode_destroy_session_maxsz) #define NFS4_enc_get_lease_time_sz (compound_encode_hdr_maxsz + \ encode_sequence_maxsz + \ encode_putrootfh_maxsz + \ @@ -1609,6 +1615,18 @@ static void encode_create_session(struct xdr_stream *xdr, hdr->nops++; hdr->replen += decode_create_session_maxsz; } + +static void encode_destroy_session(struct xdr_stream *xdr, + struct nfs4_session *session, + struct compound_hdr *hdr) +{ + __be32 *p; + RESERVE_SPACE(4 + NFS4_MAX_SESSIONID_LEN); + WRITE32(OP_DESTROY_SESSION); + WRITEMEM(session->sess_id.data, NFS4_MAX_SESSIONID_LEN); + hdr->nops++; + hdr->replen += decode_destroy_session_maxsz; +} #endif /* CONFIG_NFS_V4_1 */ static void encode_sequence(struct xdr_stream *xdr, @@ -2354,6 +2372,24 @@ static int nfs4_xdr_enc_create_session(struct rpc_rqst *req, uint32_t *p, return 0; } +/* + * a DESTROY_SESSION request + */ +static int nfs4_xdr_enc_destroy_session(struct rpc_rqst *req, uint32_t *p, + struct nfs4_session *session) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .minorversion = session->clp->cl_minorversion, + }; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, req, &hdr); + encode_destroy_session(&xdr, session, &hdr); + encode_nops(&hdr); + return 0; +} + /* * a GET_LEASE_TIME request */ @@ -4188,6 +4224,11 @@ static int decode_create_session(struct xdr_stream *xdr, status = decode_chan_attrs(xdr, &session->bc_attrs); return status; } + +static int decode_destroy_session(struct xdr_stream *xdr, void *dummy) +{ + return decode_op_hdr(xdr, OP_DESTROY_SESSION); +} #endif /* CONFIG_NFS_V4_1 */ static int decode_sequence(struct xdr_stream *xdr, @@ -5122,6 +5163,23 @@ static int nfs4_xdr_dec_create_session(struct rpc_rqst *rqstp, uint32_t *p, return status; } +/* + * a DESTROY_SESSION request + */ +static int nfs4_xdr_dec_destroy_session(struct rpc_rqst *rqstp, uint32_t *p, + void *dummy) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (!status) + status = decode_destroy_session(&xdr, dummy); + return status; +} + /* * a GET_LEASE_TIME request */ @@ -5316,6 +5374,7 @@ struct rpc_procinfo nfs4_procedures[] = { #if defined(CONFIG_NFS_V4_1) PROC(EXCHANGE_ID, enc_exchange_id, dec_exchange_id), PROC(CREATE_SESSION, enc_create_session, dec_create_session), + PROC(DESTROY_SESSION, enc_destroy_session, dec_destroy_session), PROC(GET_LEASE_TIME, enc_get_lease_time, dec_get_lease_time), #endif /* CONFIG_NFS_V4_1 */ }; diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 5a8fdc791cc..73db6f8c145 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -2485,12 +2485,14 @@ static void nfs4_kill_super(struct super_block *sb) { struct nfs_server *server = NFS_SB(sb); + dprintk("--> %s\n", __func__); nfs_super_return_all_delegations(sb); kill_anon_super(sb); nfs4_renewd_prepare_shutdown(server); nfs_fscache_release_super_cookie(sb); nfs_free_server(server); + dprintk("<-- %s\n", __func__); } /* -- cgit v1.2.3 From 8328d59f380e26477b9c1ede99e33d021365bcd1 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:35 -0400 Subject: nfs41: enable nfs_client only nfs4_async_handle_error The session is per struct nfs_client, not per nfs_server. Allow the handler to be called with no nfs_server which simplifies the nfs4_proc_async_sequence session renewal call and will let it be used by pnfs file layout data servers. Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 59e8bf5b10b..5fe208b486d 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3221,10 +3221,8 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen } static int -nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs4_state *state) +_nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs_client *clp, struct nfs4_state *state) { - struct nfs_client *clp = server->nfs_client; - if (!clp || task->tk_status >= 0) return 0; switch(task->tk_status) { @@ -3244,7 +3242,8 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, task->tk_status = 0; return -EAGAIN; case -NFS4ERR_DELAY: - nfs_inc_server_stats(server, NFSIOS_DELAY); + if (server) + nfs_inc_server_stats(server, NFSIOS_DELAY); case -NFS4ERR_GRACE: rpc_delay(task, NFS4_POLL_RETRY_MAX); task->tk_status = 0; @@ -3257,6 +3256,12 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, return 0; } +static int +nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs4_state *state) +{ + return _nfs4_async_handle_error(task, server, server->nfs_client, state); +} + int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, unsigned short port, struct rpc_cred *cred) { nfs4_verifier sc_verifier; -- cgit v1.2.3 From fc01cea963a246742ff15e118ce5259e3091352c Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:36 -0400 Subject: nfs41: sequence operation Implement the sequence operation conforming to http://tools.ietf.org/html/draft-ietf-nfsv4-minorversion1-26 Check returned sessionid, slotid and slot sequenceid in decode_sequence. If the server returns different values for sessionID, slotID or slot sequence number than what was sent, the server is looney tunes. Pass the sequence operation status to nfs41_sequence_done in order to determine when to increment the slot sequence ID. Free slot is separated from sequence done. Signed-off-by: Rahul Iyer Signed-off-by: Ricardo Labiaga Signed-off-by: Andy Adamson [nfs41: sequence res use slotid] Signed-off-by: Andy Adamson [nfs41: deref slot table in decode_sequence only for minorversion!=0] Signed-off-by: Benny Halevy [nfs41: nfs4_call_sync] [nfs41: remove SEQ4_STATUS_USE_TK_STATUS] [nfs41: return ESERVERFAULT in decode_sequence] [no sr_session, no sr_flags] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: use nfs4_call_sync_sequence to renew session lease] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: remove nfs4_call_sync_sequence forward definition] Signed-off-by: Andy Adamson [nfs41: use struct nfs_client for nfs41_proc_async_sequence] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: pass *session in seq_args and seq_res] Signed-off-by: Benny Halevy [nfs41 nfs41_sequence_call_done update error checking] [nfs41 nfs41_sequence_done update error checking] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: remove switch on error from nfs41_sequence_call_done] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 92 +++++++++++++++++++++++++++++++++++++++ fs/nfs/nfs4xdr.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 214 insertions(+), 5 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 5fe208b486d..17768095f0c 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4567,6 +4567,98 @@ int nfs4_proc_destroy_session(struct nfs4_session *session) return status; } +/* + * Renew the cl_session lease. + */ +static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred) +{ + struct nfs4_sequence_args args; + struct nfs4_sequence_res res; + + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEQUENCE], + .rpc_argp = &args, + .rpc_resp = &res, + .rpc_cred = cred, + }; + + args.sa_cache_this = 0; + + return nfs4_call_sync_sequence(clp, clp->cl_rpcclient, &msg, &args, + &res, 0); +} + +void nfs41_sequence_call_done(struct rpc_task *task, void *data) +{ + struct nfs_client *clp = (struct nfs_client *)data; + + nfs41_sequence_done(clp, task->tk_msg.rpc_resp, task->tk_status); + + if (task->tk_status < 0) { + dprintk("%s ERROR %d\n", __func__, task->tk_status); + + if (_nfs4_async_handle_error(task, NULL, clp, NULL) + == -EAGAIN) { + rpc_restart_call(task); + return; + } + } + nfs41_sequence_free_slot(clp, task->tk_msg.rpc_resp); + dprintk("%s rpc_cred %p\n", __func__, task->tk_msg.rpc_cred); + + put_rpccred(task->tk_msg.rpc_cred); + kfree(task->tk_msg.rpc_argp); + kfree(task->tk_msg.rpc_resp); + + dprintk("<-- %s\n", __func__); +} + +static void nfs41_sequence_prepare(struct rpc_task *task, void *data) +{ + struct nfs_client *clp; + struct nfs4_sequence_args *args; + struct nfs4_sequence_res *res; + + clp = (struct nfs_client *)data; + args = task->tk_msg.rpc_argp; + res = task->tk_msg.rpc_resp; + + if (nfs4_setup_sequence(clp, args, res, 0, task)) + return; + rpc_call_start(task); +} + +static const struct rpc_call_ops nfs41_sequence_ops = { + .rpc_call_done = nfs41_sequence_call_done, + .rpc_call_prepare = nfs41_sequence_prepare, +}; + +static int nfs41_proc_async_sequence(struct nfs_client *clp, + struct rpc_cred *cred) +{ + struct nfs4_sequence_args *args; + struct nfs4_sequence_res *res; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEQUENCE], + .rpc_cred = cred, + }; + + args = kzalloc(sizeof(*args), GFP_KERNEL); + if (!args) + return -ENOMEM; + res = kzalloc(sizeof(*res), GFP_KERNEL); + if (!res) { + kfree(args); + return -ENOMEM; + } + res->sr_slotid = NFS4_MAX_SLOT_TABLE; + msg.rpc_argp = args; + msg.rpc_resp = res; + + return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_SOFT, + &nfs41_sequence_ops, (void *)clp); +} + #endif /* CONFIG_NFS_V4_1 */ struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops = { diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index a81e49648cc..617273e7d47 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -296,8 +296,10 @@ static int nfs4_stat_to_errno(int); decode_channel_attrs_maxsz) #define encode_destroy_session_maxsz (op_encode_hdr_maxsz + 4) #define decode_destroy_session_maxsz (op_decode_hdr_maxsz) -#define encode_sequence_maxsz 0 /* stub */ -#define decode_sequence_maxsz 0 /* stub */ +#define encode_sequence_maxsz (op_encode_hdr_maxsz + \ + XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + 4) +#define decode_sequence_maxsz (op_decode_hdr_maxsz + \ + XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + 5) #else /* CONFIG_NFS_V4_1 */ #define encode_sequence_maxsz 0 #define decode_sequence_maxsz 0 @@ -661,6 +663,12 @@ static int nfs4_stat_to_errno(int); encode_destroy_session_maxsz) #define NFS4_dec_destroy_session_sz (compound_decode_hdr_maxsz + \ decode_destroy_session_maxsz) +#define NFS4_enc_sequence_sz \ + (compound_decode_hdr_maxsz + \ + encode_sequence_maxsz) +#define NFS4_dec_sequence_sz \ + (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz) #define NFS4_enc_get_lease_time_sz (compound_encode_hdr_maxsz + \ encode_sequence_maxsz + \ encode_putrootfh_maxsz + \ @@ -1635,11 +1643,39 @@ static void encode_sequence(struct xdr_stream *xdr, { #if defined(CONFIG_NFS_V4_1) struct nfs4_session *session = args->sa_session; + struct nfs4_slot_table *tp; + struct nfs4_slot *slot; + __be32 *p; if (!session) return; - /* stub */ + tp = &session->fc_slot_table; + + WARN_ON(args->sa_slotid == NFS4_MAX_SLOT_TABLE); + slot = tp->slots + args->sa_slotid; + + RESERVE_SPACE(4); + WRITE32(OP_SEQUENCE); + + /* + * Sessionid + seqid + slotid + max slotid + cache_this + */ + dprintk("%s: sessionid=%u:%u:%u:%u seqid=%d slotid=%d " + "max_slotid=%d cache_this=%d\n", + __func__, + ((u32 *)session->sess_id.data)[0], + ((u32 *)session->sess_id.data)[1], + ((u32 *)session->sess_id.data)[2], + ((u32 *)session->sess_id.data)[3], + slot->seq_nr, args->sa_slotid, + tp->highest_used_slotid, args->sa_cache_this); + RESERVE_SPACE(NFS4_MAX_SESSIONID_LEN + 16); + WRITEMEM(session->sess_id.data, NFS4_MAX_SESSIONID_LEN); + WRITE32(slot->seq_nr); + WRITE32(args->sa_slotid); + WRITE32(tp->highest_used_slotid); + WRITE32(args->sa_cache_this); hdr->nops++; hdr->replen += decode_sequence_maxsz; #endif /* CONFIG_NFS_V4_1 */ @@ -2390,6 +2426,24 @@ static int nfs4_xdr_enc_destroy_session(struct rpc_rqst *req, uint32_t *p, return 0; } +/* + * a SEQUENCE request + */ +static int nfs4_xdr_enc_sequence(struct rpc_rqst *req, uint32_t *p, + struct nfs4_sequence_args *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .minorversion = nfs4_xdr_minorversion(args), + }; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, req, &hdr); + encode_sequence(&xdr, args, &hdr); + encode_nops(&hdr); + return 0; +} + /* * a GET_LEASE_TIME request */ @@ -4236,13 +4290,58 @@ static int decode_sequence(struct xdr_stream *xdr, struct rpc_rqst *rqstp) { #if defined(CONFIG_NFS_V4_1) + struct nfs4_slot *slot; + struct nfs4_sessionid id; + u32 dummy; + int status; + __be32 *p; + if (!res->sr_session) return 0; - /* stub */ -#endif /* CONFIG_NFS_V4_1 */ + status = decode_op_hdr(xdr, OP_SEQUENCE); + if (status) + goto out_err; + /* + * If the server returns different values for sessionID, slotID or + * sequence number, the server is looney tunes. + */ + status = -ESERVERFAULT; + + slot = &res->sr_session->fc_slot_table.slots[res->sr_slotid]; + READ_BUF(NFS4_MAX_SESSIONID_LEN + 20); + COPYMEM(id.data, NFS4_MAX_SESSIONID_LEN); + if (memcmp(id.data, res->sr_session->sess_id.data, + NFS4_MAX_SESSIONID_LEN)) { + dprintk("%s Invalid session id\n", __func__); + goto out_err; + } + /* seqid */ + READ32(dummy); + if (dummy != slot->seq_nr) { + dprintk("%s Invalid sequence number\n", __func__); + goto out_err; + } + /* slot id */ + READ32(dummy); + if (dummy != res->sr_slotid) { + dprintk("%s Invalid slot id\n", __func__); + goto out_err; + } + /* highest slot id - currently not processed */ + READ32(dummy); + /* target highest slot id - currently not processed */ + READ32(dummy); + /* result flags - currently not processed */ + READ32(dummy); + status = 0; +out_err: + res->sr_status = status; + return status; +#else /* CONFIG_NFS_V4_1 */ return 0; +#endif /* CONFIG_NFS_V4_1 */ } /* @@ -5180,6 +5279,23 @@ static int nfs4_xdr_dec_destroy_session(struct rpc_rqst *rqstp, uint32_t *p, return status; } +/* + * a SEQUENCE request + */ +static int nfs4_xdr_dec_sequence(struct rpc_rqst *rqstp, uint32_t *p, + struct nfs4_sequence_res *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (!status) + status = decode_sequence(&xdr, res, rqstp); + return status; +} + /* * a GET_LEASE_TIME request */ @@ -5375,6 +5491,7 @@ struct rpc_procinfo nfs4_procedures[] = { PROC(EXCHANGE_ID, enc_exchange_id, dec_exchange_id), PROC(CREATE_SESSION, enc_create_session, dec_create_session), PROC(DESTROY_SESSION, enc_destroy_session, dec_destroy_session), + PROC(SEQUENCE, enc_sequence, dec_sequence), PROC(GET_LEASE_TIME, enc_get_lease_time, dec_get_lease_time), #endif /* CONFIG_NFS_V4_1 */ }; -- cgit v1.2.3 From ac72b7b3b3263ce64d55094eac1d1bde5f34e64a Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:37 -0400 Subject: nfs41: reset the session slot table Separated from nfs41: schedule async session reset Do not kfree the session slot table upon session reset, just re-initialize it. Add a boolean to nfs4_proc_create_session to inidicate if this is a session reset or a session initialization. Signed-off-by: Benny Halevy Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 17768095f0c..0b121474024 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4289,6 +4289,37 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo) return status; } +/* Reset a slot table */ +static int nfs4_reset_slot_table(struct nfs4_session *session) +{ + struct nfs4_slot_table *tbl = &session->fc_slot_table; + int i, max_slots = session->fc_attrs.max_reqs; + int old_max_slots = session->fc_slot_table.max_slots; + int ret = 0; + + dprintk("--> %s: max_reqs=%u, tbl %p\n", __func__, + session->fc_attrs.max_reqs, tbl); + + /* Until we have dynamic slot table adjustment, insist + * upon the same slot table size */ + if (max_slots != old_max_slots) { + dprintk("%s reset slot table does't match old\n", + __func__); + ret = -EINVAL; /*XXX NFS4ERR_REQ_TOO_BIG ? */ + goto out; + } + spin_lock(&tbl->slot_tbl_lock); + for (i = 0; i < max_slots; ++i) + tbl->slots[i].seq_nr = 1; + tbl->highest_used_slotid = -1; + spin_unlock(&tbl->slot_tbl_lock); + dprintk("%s: tbl=%p slots=%p max_slots=%d\n", __func__, + tbl, tbl->slots, tbl->max_slots); +out: + dprintk("<-- %s: return %d\n", __func__, ret); + return ret; +} + /* * Initialize slot table */ @@ -4497,7 +4528,7 @@ static int _nfs4_proc_create_session(struct nfs_client *clp) * It is the responsibility of the caller to verify the session is * expired before calling this routine. */ -int nfs4_proc_create_session(struct nfs_client *clp) +int nfs4_proc_create_session(struct nfs_client *clp, int reset) { int status; unsigned *ptr; @@ -4510,8 +4541,11 @@ int nfs4_proc_create_session(struct nfs_client *clp) if (status) goto out; - /* Init the fore channel */ - status = nfs4_init_slot_table(session); + /* Init or reset the fore channel */ + if (reset) + status = nfs4_reset_slot_table(session); + else + status = nfs4_init_slot_table(session); dprintk("fore channel slot table initialization returned %d\n", status); if (status) goto out; @@ -4520,6 +4554,10 @@ int nfs4_proc_create_session(struct nfs_client *clp) dprintk("%s client>seqid %d sessionid %u:%u:%u:%u\n", __func__, clp->cl_seqid, ptr[0], ptr[1], ptr[2], ptr[3]); + if (reset) + /* Lease time is aleady set */ + goto out; + /* Get the lease time */ status = nfs4_proc_get_lease_time(clp, &fsinfo); if (status == 0) { -- cgit v1.2.3 From 76db6d9500caeaa774a3e32a997eba30bbdc176b Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:38 -0400 Subject: nfs41: add session setup to the state manager At mount, nfs_alloc_client sets the cl_state NFS4CLNT_LEASE_EXPIRED bit and nfs4_alloc_session sets the NFS4CLNT_SESSION_SETUP bit, so both bits are set when nfs4_lookup_root calls nfs4_recover_expired_lease which schedules the nfs4_state_manager and waits for it to complete. Place the session setup after the clientid establishment in nfs4_state_manager so that the session is setup right after the clientid has been established without rescheduling the state manager. Unlike nfsv4.0, the nfs_client struct is not ready to use until the session has been established. Postpone marking the nfs_client struct to NFS_CS_READY until after a successful CREATE_SESSION call so that other threads cannot use the client until the session is established. If the EXCHANGE_ID call fails and the session has not been setup (the NFS4CLNT_SESSION_SETUP bit is set), mark the client with the error and return. If the session setup CREATE_SESSION call fails with NFS4ERR_STALE_CLIENTID which could occur due to server reboot or network partition inbetween the EXCHANGE_ID and CREATE_SESSION call, reset the NFS4CLNT_LEASE_EXPIRED and NFS4CLNT_SESSION_SETUP bits and try again. If the CREATE_SESSION call fails with other errors, mark the client with the error and return. Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: NFS_CS_SESSION_SETUP cl_cons_state for back channel setup] On session setup, the CREATE_SESSION reply races with the server back channel probe which needs to succeed to setup the back channel. Set a new cl_cons_state NFS_CS_SESSION_SETUP just prior to the CREATE_SESSION call and add it as a valid state to nfs_find_client so that the client back channel can find the nfs_client struct and won't drop the server backchannel probe. Use a new cl_cons_state so that NFSv4.0 back channel behaviour which only sets NFS_CS_READY is unchanged. Adjust waiting on the nfs_client_active_wq accordingly. Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: rename NFS_CS_SESSION_SETUP to NFS_CS_SESSION_INITING] Signed-off-by: Andy Adamson [nfs41: set NFS_CL_SESSION_INITING in alloc_session] Signed-off-by: Andy Adamson [nfs41: move session setup into a function] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [moved nfs4_proc_create_session declaration here] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 10 ++++++---- fs/nfs/internal.h | 1 + fs/nfs/nfs4_fs.h | 2 ++ fs/nfs/nfs4proc.c | 10 ++++++++++ fs/nfs/nfs4state.c | 35 ++++++++++++++++++++++++++++++++++- include/linux/nfs_fs_sb.h | 1 + 6 files changed, 54 insertions(+), 5 deletions(-) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index bb7432d83b5..d28a987f569 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -366,7 +366,8 @@ struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion) struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; /* Don't match clients that failed to initialise properly */ - if (clp->cl_cons_state != NFS_CS_READY) + if (!(clp->cl_cons_state == NFS_CS_READY || + clp->cl_cons_state == NFS_CS_SESSION_INITING)) continue; /* Different NFS versions cannot share the same nfs_client */ @@ -499,7 +500,7 @@ found_client: nfs_free_client(new); error = wait_event_killable(nfs_client_active_wq, - clp->cl_cons_state != NFS_CS_INITING); + clp->cl_cons_state < NFS_CS_INITING); if (error < 0) { nfs_put_client(clp); return ERR_PTR(-ERESTARTSYS); @@ -520,7 +521,7 @@ found_client: /* * Mark a server as ready or failed */ -static void nfs_mark_client_ready(struct nfs_client *clp, int state) +void nfs_mark_client_ready(struct nfs_client *clp, int state) { clp->cl_cons_state = state; wake_up_all(&nfs_client_active_wq); @@ -1135,7 +1136,8 @@ static int nfs4_init_client(struct nfs_client *clp, if (error < 0) goto error; - nfs_mark_client_ready(clp, NFS_CS_READY); + if (!nfs4_has_session(clp)) + nfs_mark_client_ready(clp, NFS_CS_READY); return 0; error: diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index f62bc522615..f3b310e8ea0 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -100,6 +100,7 @@ extern void nfs_free_server(struct nfs_server *server); extern struct nfs_server *nfs_clone_server(struct nfs_server *, struct nfs_fh *, struct nfs_fattr *); +extern void nfs_mark_client_ready(struct nfs_client *clp, int state); #ifdef CONFIG_PROC_FS extern int __init nfs_fs_proc_init(void); extern void nfs_fs_proc_exit(void); diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index eccf4e93e7d..288717abadd 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -44,6 +44,7 @@ enum nfs4_client_state { NFS4CLNT_RECLAIM_REBOOT, NFS4CLNT_RECLAIM_NOGRACE, NFS4CLNT_DELEGRETURN, + NFS4CLNT_SESSION_SETUP, }; /* @@ -208,6 +209,7 @@ extern int nfs4_setup_sequence(struct nfs_client *clp, int cache_reply, struct rpc_task *task); extern void nfs4_destroy_session(struct nfs4_session *session); extern struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp); +extern int nfs4_proc_create_session(struct nfs_client *, int reset); #else /* CONFIG_NFS_v4_1 */ static inline int nfs4_setup_sequence(struct nfs_client *clp, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 0b121474024..7fc0c9c8f5e 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4382,6 +4382,16 @@ struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp) session = kzalloc(sizeof(struct nfs4_session), GFP_KERNEL); if (!session) return NULL; + + set_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state); + /* + * The create session reply races with the server back + * channel probe. Mark the client NFS_CS_SESSION_INITING + * so that the client back channel can find the + * nfs_client struct + */ + clp->cl_cons_state = NFS_CS_SESSION_INITING; + tbl = &session->fc_slot_table; spin_lock_init(&tbl->slot_tbl_lock); rpc_init_wait_queue(&tbl->slot_tbl_waitq, "Slot table"); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index bc683ed477e..df5b4807daa 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1113,6 +1113,27 @@ static int nfs4_reclaim_lease(struct nfs_client *clp) return status; } +#ifdef CONFIG_NFS_V4_1 + +static int nfs4_initialize_session(struct nfs_client *clp) +{ + int status; + + status = nfs4_proc_create_session(clp, 0); + if (!status) { + nfs_mark_client_ready(clp, NFS_CS_READY); + } else if (status == -NFS4ERR_STALE_CLIENTID) { + set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); + set_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state); + } else { + nfs_mark_client_ready(clp, status); + } + return status; +} +#else /* CONFIG_NFS_V4_1 */ +static int nfs4_initialize_session(struct nfs_client *clp) { return 0; } +#endif /* CONFIG_NFS_V4_1 */ + static void nfs4_state_manager(struct nfs_client *clp) { int status = 0; @@ -1126,6 +1147,9 @@ static void nfs4_state_manager(struct nfs_client *clp) set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); if (status == -EAGAIN) continue; + if (clp->cl_cons_state == + NFS_CS_SESSION_INITING) + nfs_mark_client_ready(clp, status); goto out_error; } clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state); @@ -1136,7 +1160,16 @@ static void nfs4_state_manager(struct nfs_client *clp) if (status != 0) continue; } - + /* Setup the session */ + if (nfs4_has_session(clp) && + test_and_clear_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state)) { + status = nfs4_initialize_session(clp); + if (status) { + if (status == -NFS4ERR_STALE_CLIENTID) + continue; + goto out_error; + } + } /* First recover reboot state... */ if (test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) { status = nfs4_do_reclaim(clp, &nfs4_reboot_recovery_ops); diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 435ed556efb..d0902ccec9c 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -24,6 +24,7 @@ struct nfs_client { int cl_cons_state; /* current construction state (-ve: init error) */ #define NFS_CS_READY 0 /* ready to be used */ #define NFS_CS_INITING 1 /* busy initialising */ +#define NFS_CS_SESSION_INITING 2 /* busy initialising session */ unsigned long cl_res_state; /* NFS resources state */ #define NFS_CS_CALLBACK 1 /* - callback started */ #define NFS_CS_IDMAP 2 /* - idmap started */ -- cgit v1.2.3 From c3fad1b1aaf850bf692642642ace7cd0d64af0a3 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:39 -0400 Subject: nfs41: add session reset to state manager Move the code to reset a session from the session_reclaimer to the nfs4_state_manager. Destroy the session, and create a new one. Treat NFS4ERR_BADSESSION and NFS4ERR_DEADSESSION as a successful nfs4_proc_destroy_session. Signal nfs4_proc_create_session that this is a session reset so that the session slot table is re-used. If the clientid is stale, set both NFS4CLNT_LEASE_EXPIRED and NFS4CLNT_SESSION_SETUP bits and retry. Use a switch statement in nfs4_session_recovery_handle_error for future patche which will add handling for other errors. Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: session reset in nfs4_recovery_handle_error] Signed-off-by: Andy Adamson [nfs41: reset session on nfs4_do_reclaim session reset error] If nfs4_do_reclaim gets a session reset error, nfs4_recovery_handle_error will set the NFS4CLNT_SESSION_SETUP bit, and the state manager should continue processing to reset the session. Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [move nfs4_proc_destroy_session declaration here] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 1 + fs/nfs/nfs4state.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 288717abadd..7cc1cddfd9b 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -210,6 +210,7 @@ extern int nfs4_setup_sequence(struct nfs_client *clp, extern void nfs4_destroy_session(struct nfs4_session *session); extern struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp); extern int nfs4_proc_create_session(struct nfs_client *, int reset); +extern int nfs4_proc_destroy_session(struct nfs4_session *); #else /* CONFIG_NFS_v4_1 */ static inline int nfs4_setup_sequence(struct nfs_client *clp, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index df5b4807daa..8b7f007adc4 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1042,6 +1042,14 @@ static void nfs4_recovery_handle_error(struct nfs_client *clp, int error) case -NFS4ERR_EXPIRED: set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); nfs4_state_start_reclaim_nograce(clp); + case -NFS4ERR_BADSESSION: + case -NFS4ERR_BADSLOT: + case -NFS4ERR_BAD_HIGH_SLOT: + case -NFS4ERR_DEADSESSION: + case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: + case -NFS4ERR_SEQ_FALSE_RETRY: + case -NFS4ERR_SEQ_MISORDERED: + set_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state); } } @@ -1114,6 +1122,36 @@ static int nfs4_reclaim_lease(struct nfs_client *clp) } #ifdef CONFIG_NFS_V4_1 +static void nfs4_session_recovery_handle_error(struct nfs_client *clp, int err) +{ + switch (err) { + case -NFS4ERR_STALE_CLIENTID: + set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); + set_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state); + } +} + +static int nfs4_reset_session(struct nfs_client *clp) +{ + int status; + + status = nfs4_proc_destroy_session(clp->cl_session); + if (status && status != -NFS4ERR_BADSESSION && + status != -NFS4ERR_DEADSESSION) { + nfs4_session_recovery_handle_error(clp, status); + goto out; + } + + memset(clp->cl_session->sess_id.data, 0, NFS4_MAX_SESSIONID_LEN); + status = nfs4_proc_create_session(clp, 1); + if (status) + nfs4_session_recovery_handle_error(clp, status); + /* fall through*/ +out: + /* Wake up the next rpc task even on error */ + rpc_wake_up_next(&clp->cl_session->fc_slot_table.slot_tbl_waitq); + return status; +} static int nfs4_initialize_session(struct nfs_client *clp) { @@ -1131,6 +1169,7 @@ static int nfs4_initialize_session(struct nfs_client *clp) return status; } #else /* CONFIG_NFS_V4_1 */ +static int nfs4_reset_session(struct nfs_client *clp) { return 0; } static int nfs4_initialize_session(struct nfs_client *clp) { return 0; } #endif /* CONFIG_NFS_V4_1 */ @@ -1160,10 +1199,13 @@ static void nfs4_state_manager(struct nfs_client *clp) if (status != 0) continue; } - /* Setup the session */ + /* Initialize or reset the session */ if (nfs4_has_session(clp) && test_and_clear_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state)) { - status = nfs4_initialize_session(clp); + if (clp->cl_cons_state == NFS_CS_SESSION_INITING) + status = nfs4_initialize_session(clp); + else + status = nfs4_reset_session(clp); if (status) { if (status == -NFS4ERR_STALE_CLIENTID) continue; @@ -1175,6 +1217,8 @@ static void nfs4_state_manager(struct nfs_client *clp) status = nfs4_do_reclaim(clp, &nfs4_reboot_recovery_ops); if (status == -NFS4ERR_STALE_CLIENTID) continue; + if (test_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state)) + continue; nfs4_state_end_reclaim_reboot(clp); continue; } @@ -1188,6 +1232,9 @@ static void nfs4_state_manager(struct nfs_client *clp) continue; if (status == -NFS4ERR_EXPIRED) continue; + if (test_bit(NFS4CLNT_SESSION_SETUP, + &clp->cl_state)) + continue; goto out_error; } else nfs4_state_end_reclaim_nograce(clp); -- cgit v1.2.3 From aae2006e9b0c294114915c13022fa348e1a88023 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:40 -0400 Subject: nfs41: sunrpc: Export the call prepare state for session reset Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- include/linux/sunrpc/clnt.h | 1 + include/linux/sunrpc/sched.h | 1 + net/sunrpc/clnt.c | 13 +++++++++++++ net/sunrpc/sched.c | 2 +- 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index c39a21040dc..37881f1a0bd 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -143,6 +143,7 @@ int rpc_call_sync(struct rpc_clnt *clnt, const struct rpc_message *msg, int flags); struct rpc_task *rpc_call_null(struct rpc_clnt *clnt, struct rpc_cred *cred, int flags); +void rpc_restart_call_prepare(struct rpc_task *); void rpc_restart_call(struct rpc_task *); void rpc_setbufsize(struct rpc_clnt *, unsigned int, unsigned int); size_t rpc_max_payload(struct rpc_clnt *); diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index 64981a2f1ca..177376880fa 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -237,6 +237,7 @@ void rpc_show_tasks(void); int rpc_init_mempool(void); void rpc_destroy_mempool(void); extern struct workqueue_struct *rpciod_workqueue; +void rpc_prepare_task(struct rpc_task *task); static inline void rpc_exit(struct rpc_task *task, int status) { diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 5abab094441..d00e8135f86 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -694,6 +694,19 @@ void rpc_force_rebind(struct rpc_clnt *clnt) } EXPORT_SYMBOL_GPL(rpc_force_rebind); +/* + * Restart an (async) RPC call from the call_prepare state. + * Usually called from within the exit handler. + */ +void +rpc_restart_call_prepare(struct rpc_task *task) +{ + if (RPC_ASSASSINATED(task)) + return; + task->tk_action = rpc_prepare_task; +} +EXPORT_SYMBOL_GPL(rpc_restart_call_prepare); + /* * Restart an (async) RPC call. Usually called from within the * exit handler. diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index ff50a054686..1102ce1251f 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -569,7 +569,7 @@ EXPORT_SYMBOL_GPL(rpc_delay); /* * Helper to call task->tk_ops->rpc_call_prepare */ -static void rpc_prepare_task(struct rpc_task *task) +void rpc_prepare_task(struct rpc_task *task) { task->tk_ops->rpc_call_prepare(task, task->tk_calldata); } -- cgit v1.2.3 From eedc020e718b8ce45381383ec66030f09eb02a1e Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:41 -0400 Subject: nfs41: use rpc prepare call state for session reset [nfs41: change nfs4_restart_rpc argument] [nfs41: check for session not minorversion] [nfs41: trigger the state manager for session reset] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [always define nfs4_restart_rpc] Signed-off-by: Trond Myklebust --- fs/nfs/internal.h | 39 +++++++++++++++++++++++++++------------ fs/nfs/nfs4proc.c | 15 ++++++++------- fs/nfs/read.c | 2 +- fs/nfs/unlink.c | 2 +- fs/nfs/write.c | 8 ++++---- 5 files changed, 41 insertions(+), 25 deletions(-) diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index f3b310e8ea0..78508f29c03 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -2,6 +2,7 @@ * NFS internal definitions */ +#include "nfs4_fs.h" #include #include @@ -17,6 +18,18 @@ struct nfs_string; */ #define NFS_MAX_READAHEAD (RPC_DEF_SLOT_TABLE - 1) +/* + * Determine if sessions are in use. + */ +static inline int nfs4_has_session(const struct nfs_client *clp) +{ +#ifdef CONFIG_NFS_V4_1 + if (clp->cl_session) + return 1; +#endif /* CONFIG_NFS_V4_1 */ + return 0; +} + struct nfs_clone_mount { const struct super_block *sb; const struct dentry *dentry; @@ -148,6 +161,20 @@ 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 */ +static inline void nfs4_restart_rpc(struct rpc_task *task, + const struct nfs_client *clp) +{ +#ifdef CONFIG_NFS_V4_1 + if (nfs4_has_session(clp) && + test_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state)) { + rpc_restart_call_prepare(task); + return; + } +#endif /* CONFIG_NFS_V4_1 */ + rpc_restart_call(task); +} + /* nfs4xdr.c */ #ifdef CONFIG_NFS_V4 extern __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus); @@ -225,18 +252,6 @@ extern int _nfs4_call_sync_session(struct nfs_server *server, struct nfs4_sequence_res *res, int cache_reply); -/* - * Determine if sessions are in use. - */ -static inline int nfs4_has_session(const struct nfs_client *clp) -{ -#ifdef CONFIG_NFS_V4_1 - if (clp->cl_session) - return 1; -#endif /* CONFIG_NFS_V4_1 */ - return 0; -} - #ifdef CONFIG_NFS_V4_1 extern void nfs41_sequence_free_slot(const struct nfs_client *, struct nfs4_sequence_res *res); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 7fc0c9c8f5e..2d45606a808 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1661,7 +1661,7 @@ static void nfs4_close_done(struct rpc_task *task, void *data) break; default: if (nfs4_async_handle_error(task, server, state) == -EAGAIN) { - rpc_restart_call(task); + nfs4_restart_rpc(task, server->nfs_client); return; } } @@ -2874,7 +2874,7 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data) nfs4_sequence_done(server, &data->res.seq_res, task->tk_status); if (nfs4_async_handle_error(task, server, data->args.context->state) == -EAGAIN) { - rpc_restart_call(task); + nfs4_restart_rpc(task, server->nfs_client); return -EAGAIN; } @@ -2899,7 +2899,7 @@ static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data) task->tk_status); if (nfs4_async_handle_error(task, NFS_SERVER(inode), data->args.context->state) == -EAGAIN) { - rpc_restart_call(task); + nfs4_restart_rpc(task, NFS_SERVER(inode)->nfs_client); return -EAGAIN; } if (task->tk_status >= 0) { @@ -2927,7 +2927,7 @@ static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data) nfs4_sequence_done(NFS_SERVER(inode), &data->res.seq_res, task->tk_status); if (nfs4_async_handle_error(task, NFS_SERVER(inode), NULL) == -EAGAIN) { - rpc_restart_call(task); + nfs4_restart_rpc(task, NFS_SERVER(inode)->nfs_client); return -EAGAIN; } nfs4_sequence_free_slot(NFS_SERVER(inode)->nfs_client, @@ -3628,7 +3628,8 @@ static void nfs4_locku_done(struct rpc_task *task, void *data) break; default: if (nfs4_async_handle_error(task, calldata->server, NULL) == -EAGAIN) - rpc_restart_call(task); + nfs4_restart_rpc(task, + calldata->server->nfs_client); } nfs4_sequence_free_slot(calldata->server->nfs_client, &calldata->res.seq_res); @@ -4237,7 +4238,7 @@ static void nfs4_get_lease_time_done(struct rpc_task *task, void *calldata) dprintk("%s Retry: tk_status %d\n", __func__, task->tk_status); rpc_delay(task, NFS4_POLL_RETRY_MIN); task->tk_status = 0; - rpc_restart_call(task); + nfs4_restart_rpc(task, data->clp); return; } nfs41_sequence_free_slot(data->clp, &data->res->lr_seq_res); @@ -4647,7 +4648,7 @@ void nfs41_sequence_call_done(struct rpc_task *task, void *data) if (_nfs4_async_handle_error(task, NULL, clp, NULL) == -EAGAIN) { - rpc_restart_call(task); + nfs4_restart_rpc(task, clp); return; } } diff --git a/fs/nfs/read.c b/fs/nfs/read.c index d71f0d55ebd..96c4ebfa46f 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -371,7 +371,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)->nfs_client); return; out: nfs4_sequence_free_slot(NFS_SERVER(data->inode)->nfs_client, diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index 089a21b4e3f..1064c91ae81 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c @@ -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)->nfs_client); } /** diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 85a76409de1..ce728829f79 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -1145,6 +1145,7 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) { struct nfs_writeargs *argp = &data->args; struct nfs_writeres *resp = &data->res; + struct nfs_server *server = NFS_SERVER(data->inode); int status; dprintk("NFS: %5u nfs_writeback_done (status %d)\n", @@ -1177,7 +1178,7 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) if (time_before(complain, jiffies)) { dprintk("NFS: faulty NFS server %s:" " (committed = %d) != (stable = %d)\n", - NFS_SERVER(data->inode)->nfs_client->cl_hostname, + server->nfs_client->cl_hostname, resp->verf->committed, argp->stable); complain = jiffies + 300 * HZ; } @@ -1203,7 +1204,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, server->nfs_client); return -EAGAIN; } if (time_before(complain, jiffies)) { @@ -1215,8 +1216,7 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) /* Can't do anything about it except throw an error. */ task->tk_status = -EIO; } - nfs4_sequence_free_slot(NFS_SERVER(data->inode)->nfs_client, - &data->res.seq_res); + nfs4_sequence_free_slot(server->nfs_client, &data->res.seq_res); return 0; } -- cgit v1.2.3 From 4745e3154bf33048d970d1519703089493988f65 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:42 -0400 Subject: nfs41: kick start nfs41 session recovery when handling errors Remove checking for any errors that the SEQUENCE operation does not return. -NFS4ERR_STALE_CLIENTID, NFS4ERR_EXPIRED, NFS4ERR_CB_PATH_DOWN, NFS4ERR_BACK_CHAN_BUSY, NFS4ERR_OP_NOT_IN_SESSION. SEQUENCE operation error recovery is very primative, we only reset the session. Remove checking for any errors that are returned by the SEQUENCE operation, but that resetting the session won't address. NFS4ERR_RETRY_UNCACHED_REP, NFS4ERR_SEQUENCE_POS,NFS4ERR_TOO_MANY_OPS. Add error checking for missing SEQUENCE errors that a session reset will address. NFS4ERR_BAD_HIGH_SLOT, NFS4ERR_DEADSESSION, NFS4ERR_SEQ_FALSE_RETRY. A reset of the session is currently our only response to a SEQUENCE operation error. Don't reset the session on errors where a new session won't help. Don't reset the session on errors where a new session won't help. [nfs41: nfs4_async_handle_error update error checking] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [nfs41: trigger the state manager for session reset] Replace session state bit with nfs_client state bit. Set the NFS4CLNT_SESSION_SETUP bit upon a session related error in the sync/async error handlers. [nfs41: _nfs4_async_handle_error fix session reset error list] Sequence operation errors that session reset could help. NFS4ERR_BADSESSION NFS4ERR_BADSLOT NFS4ERR_BAD_HIGH_SLOT NFS4ERR_DEADSESSION NFS4ERR_CONN_NOT_BOUND_TO_SESSION NFS4ERR_SEQ_FALSE_RETRY NFS4ERR_SEQ_MISORDERED Sequence operation errors that a session reset would not help NFS4ERR_BADXDR NFS4ERR_DELAY NFS4ERR_REP_TOO_BIG NFS4ERR_REP_TOO_BIG_TO_CACHE NFS4ERR_REQ_TOO_BIG NFS4ERR_RETRY_UNCACHED_REP NFS4ERR_SEQUENCE_POS NFS4ERR_TOO_MANY_OPS Signed-off-by: Andy Adamson [nfs41 nfs4_handle_exception fix session reset error list] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [moved nfs41_sequece_call_done code to nfs41: sequence operation] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 2d45606a808..ed6c8899806 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -249,7 +249,25 @@ static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, ret = nfs4_wait_clnt_recover(clp); if (ret == 0) exception->retry = 1; +#if !defined(CONFIG_NFS_V4_1) break; +#else /* !defined(CONFIG_NFS_V4_1) */ + if (!nfs4_has_session(server->nfs_client)) + break; + /* FALLTHROUGH */ + case -NFS4ERR_BADSESSION: + case -NFS4ERR_BADSLOT: + case -NFS4ERR_BAD_HIGH_SLOT: + case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: + case -NFS4ERR_DEADSESSION: + case -NFS4ERR_SEQ_FALSE_RETRY: + case -NFS4ERR_SEQ_MISORDERED: + dprintk("%s ERROR: %d Reset session\n", __func__, + errorcode); + set_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state); + exception->retry = 1; + /* FALLTHROUGH */ +#endif /* !defined(CONFIG_NFS_V4_1) */ case -NFS4ERR_FILE_OPEN: case -NFS4ERR_GRACE: case -NFS4ERR_DELAY: @@ -3241,6 +3259,20 @@ _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_BAD_HIGH_SLOT: + case -NFS4ERR_DEADSESSION: + case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: + case -NFS4ERR_SEQ_FALSE_RETRY: + case -NFS4ERR_SEQ_MISORDERED: + dprintk("%s ERROR %d, Reset session\n", __func__, + task->tk_status); + set_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state); + task->tk_status = 0; + return -EAGAIN; +#endif /* CONFIG_NFS_V4_1 */ case -NFS4ERR_DELAY: if (server) nfs_inc_server_stats(server, NFSIOS_DELAY); -- cgit v1.2.3 From b069d94af7785750228287c35f9ce5c463515726 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:43 -0400 Subject: nfs41: schedule async session reset Define a new session reset state which is set upon a sequence operation error in both the sync and async error handlers. Place all new requests and all but the last outstanding rpc on the slot_tbl_waitq. Spawn the recovery thread when the last slot is free. Call nfs4_proc_destroy_session, reinitialize the session, call nfs4_proc_create_session, clear the session reset state, and wake up the next task on the slot_tbl_waitq. Return the nfs4_proc_destroy_session status to the session reclaimer and check for NFS4ERR_BADSESSION and NFS4ERR_DEADSESSION. Other destroy session errors should be handled in nfs4_proc_destroy_session where the call can be retried with adjusted arguments. Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy nfs41: make nfs4_wait_bit_killable public] nfs4_wait_bit_killable to be used by NFSv4.1 session recover logic. Signed-off-by: Rahul Iyer Signed-off-by: Benny Halevy [nfs41: have create_session work on nfs_client] Signed-off-by: Andy Adamson [nfs41: trigger the state manager for session reset] Replace the session reset state with the NFS4CLNT_SESSION_SETUP cl_state. Place all rpc tasks to sleep on the slot table waitqueue until the slot table is drained, then schedule state recovery and wait for it to complete. Signed-off-by: Andy Adamson [nfs41: remove nfs41_session_recovery [ch] Replaced by using the nfs4_state_manager. Signed-off-by: Andy Adamson [nfs41: nfs4_wait_bit_killable only used locally] [nfs41: keep nfs4_wait_bit_killable static] [nfs41: keep const nfs_server in nfs4_handle_exception] [nfs41: remove session parameter from nfs4_find_slot] Signed-off-by: Andy Adamson [nfs41: resset the session from nfs41_setup_sequence] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index ed6c8899806..a2855391b07 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -423,6 +423,22 @@ out: return ret_id; } +static int nfs4_recover_session(struct nfs4_session *session) +{ + struct nfs_client *clp = session->clp; + int ret; + + for (;;) { + ret = nfs4_wait_clnt_recover(clp); + if (ret != 0) + return ret; + if (!test_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state)) + break; + nfs4_schedule_state_manager(clp); + } + return 0; +} + static int nfs41_setup_sequence(struct nfs4_session *session, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, @@ -431,6 +447,7 @@ static int nfs41_setup_sequence(struct nfs4_session *session, { struct nfs4_slot *slot; struct nfs4_slot_table *tbl; + int status = 0; u8 slotid; dprintk("--> %s\n", __func__); @@ -443,6 +460,23 @@ static int nfs41_setup_sequence(struct nfs4_session *session, tbl = &session->fc_slot_table; spin_lock(&tbl->slot_tbl_lock); + if (test_bit(NFS4CLNT_SESSION_SETUP, &session->clp->cl_state)) { + if (tbl->highest_used_slotid != -1) { + rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL); + spin_unlock(&tbl->slot_tbl_lock); + dprintk("<-- %s: Session reset: draining\n", __func__); + return -EAGAIN; + } + + /* The slot table is empty; start the reset thread */ + dprintk("%s Session Reset\n", __func__); + spin_unlock(&tbl->slot_tbl_lock); + status = nfs4_recover_session(session); + if (status) + return status; + spin_lock(&tbl->slot_tbl_lock); + } + slotid = nfs4_find_slot(tbl, task); if (slotid == NFS4_MAX_SLOT_TABLE) { rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL); -- cgit v1.2.3 From 29fba38b793225798104a1ac870e6807ebd078dd Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:22:44 -0400 Subject: nfs41: lease renewal Send a NFSv4.1 SEQUENCE op rather than RENEW that was deprecated in minorversion 1. Use the nfs_client minorversion to select reboot_recover/ network_partition_recovery/state_renewal ops. Note: we use reclaimer to create the nfs41 session before there are any cl_superblocks for the nfs_client. Signed-off-by: Benny Halevy [nfs41: check for session not minorversion] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [revamped patch for new nfs4_state_manager design] Signed-off-by: Benny Halevy [nfs41: obliterate nfs4_state_recovery_ops.renew_lease method] moved to nfs4_state_maintenance_ops [also undid per-minorversion nfs4_state_recovery_ops here] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 6 ++++++ fs/nfs/nfs4proc.c | 24 ++++++++++++++++++++++++ fs/nfs/nfs4renewd.c | 4 +++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 7cc1cddfd9b..ae25cc21fe7 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -180,6 +180,10 @@ struct nfs4_state_recovery_ops { int (*recover_lock)(struct nfs4_state *, struct file_lock *); }; +struct nfs4_state_maintenance_ops { + int (*sched_state_renewal)(struct nfs_client *, struct rpc_cred *); +}; + extern const struct dentry_operations nfs4_dentry_operations; extern const struct inode_operations nfs4_dir_inode_operations; @@ -220,6 +224,8 @@ static inline int nfs4_setup_sequence(struct nfs_client *clp, } #endif /* CONFIG_NFS_V4_1 */ +extern struct nfs4_state_maintenance_ops *nfs4_state_renewal_ops[]; + extern const u32 nfs4_fattr_bitmap[2]; extern const u32 nfs4_statfs_bitmap[2]; extern const u32 nfs4_pathconf_bitmap[2]; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a2855391b07..101ba6aed10 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3016,6 +3016,9 @@ static void nfs4_renew_done(struct rpc_task *task, void *data) if (time_before(clp->cl_last_renewal,timestamp)) clp->cl_last_renewal = timestamp; spin_unlock(&clp->cl_lock); + dprintk("%s calling put_rpccred on rpc_cred %p\n", __func__, + task->tk_msg.rpc_cred); + put_rpccred(task->tk_msg.rpc_cred); } static const struct rpc_call_ops nfs4_renew_ops = { @@ -4790,6 +4793,27 @@ struct nfs4_state_recovery_ops nfs4_nograce_recovery_ops = { .recover_lock = nfs4_lock_expired, }; +struct nfs4_state_maintenance_ops nfs40_state_renewal_ops = { + .sched_state_renewal = nfs4_proc_async_renew, +}; + +#if defined(CONFIG_NFS_V4_1) +struct nfs4_state_maintenance_ops nfs41_state_renewal_ops = { + .sched_state_renewal = nfs41_proc_async_sequence, +}; +#endif + +/* + * Per minor version reboot and network partition recovery ops + */ + +struct nfs4_state_maintenance_ops *nfs4_state_renewal_ops[] = { + &nfs40_state_renewal_ops, +#if defined(CONFIG_NFS_V4_1) + &nfs41_state_renewal_ops, +#endif +}; + static const struct inode_operations nfs4_file_inode_operations = { .permission = nfs_permission, .getattr = nfs_getattr, diff --git a/fs/nfs/nfs4renewd.c b/fs/nfs/nfs4renewd.c index f524e932ff7..92b43e87267 100644 --- a/fs/nfs/nfs4renewd.c +++ b/fs/nfs/nfs4renewd.c @@ -59,12 +59,14 @@ void nfs4_renew_state(struct work_struct *work) { + struct nfs4_state_maintenance_ops *ops; struct nfs_client *clp = container_of(work, struct nfs_client, cl_renewd.work); struct rpc_cred *cred; long lease, timeout; unsigned long last, now; + ops = nfs4_state_renewal_ops[clp->cl_minorversion]; dprintk("%s: start\n", __func__); /* Are there any active superblocks? */ if (list_empty(&clp->cl_superblocks)) @@ -86,7 +88,7 @@ nfs4_renew_state(struct work_struct *work) nfs_expire_all_delegations(clp); } else { /* Queue an asynchronous RENEW. */ - nfs4_proc_async_renew(clp, cred); + ops->sched_state_renewal(clp, cred); put_rpccred(cred); } timeout = (2 * lease) / 3; -- cgit v1.2.3 From 8e69514f2981d85108c2bd220ff8e188c0c27cdb Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:22:45 -0400 Subject: nfs41: support minorversion 1 for nfs4_check_lease [moved nfs4_get_renew_cred related changes to "nfs41: introduce get_state_renewal_cred"] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 1 + fs/nfs/nfs4proc.c | 2 ++ fs/nfs/nfs4state.c | 4 +++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index ae25cc21fe7..095bf4690e8 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -182,6 +182,7 @@ struct nfs4_state_recovery_ops { struct nfs4_state_maintenance_ops { int (*sched_state_renewal)(struct nfs_client *, struct rpc_cred *); + int (*renew_lease)(struct nfs_client *, struct rpc_cred *); }; extern const struct dentry_operations nfs4_dentry_operations; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 101ba6aed10..da481bda44f 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4795,11 +4795,13 @@ struct nfs4_state_recovery_ops nfs4_nograce_recovery_ops = { struct nfs4_state_maintenance_ops nfs40_state_renewal_ops = { .sched_state_renewal = nfs4_proc_async_renew, + .renew_lease = nfs4_proc_renew, }; #if defined(CONFIG_NFS_V4_1) struct nfs4_state_maintenance_ops nfs41_state_renewal_ops = { .sched_state_renewal = nfs41_proc_async_sequence, + .renew_lease = nfs4_proc_sequence, }; #endif diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 8b7f007adc4..dc5599c7f4d 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1083,6 +1083,8 @@ restart: static int nfs4_check_lease(struct nfs_client *clp) { struct rpc_cred *cred; + struct nfs4_state_maintenance_ops *ops = + nfs4_state_renewal_ops[clp->cl_minorversion]; int status = -NFS4ERR_EXPIRED; /* Is the client already known to have an expired lease? */ @@ -1094,7 +1096,7 @@ static int nfs4_check_lease(struct nfs_client *clp) if (cred == NULL) goto out; } - status = nfs4_proc_renew(clp, cred); + status = ops->renew_lease(clp, cred); put_rpccred(cred); out: nfs4_recovery_handle_error(clp, status); -- cgit v1.2.3 From a7b721037f898b29a8083da59b1dccd3da385b07 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:46 -0400 Subject: nfs41: introduce get_state_renewal_cred Use the machine cred for sending SEQUENCE to renew the client's lease. [revamp patch for new state management design starting 2.6.29] [nfs41: support minorversion 1 for nfs4_check_lease] Signed-off-by: Benny Halevy [nfs41: get cred in exchange_id when cred arg is NULL] Signed-off-by: Benny Halevy [nfs41: use cl_machined_cred instead of cl_ex_cred] Since EXCHANGE_ID insists on using the machine credential, cl_ex_cred is not needed. nfs4_proc_exchange_id() is only called if the machine credential is available. Remove the credential logic from nfs4_proc_exchange_id. Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 5 +++++ fs/nfs/nfs4proc.c | 3 +++ fs/nfs/nfs4renewd.c | 2 +- fs/nfs/nfs4state.c | 18 +++++------------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 095bf4690e8..2b141c5758e 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -182,6 +182,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_locked)(struct nfs_client *); int (*renew_lease)(struct nfs_client *, struct rpc_cred *); }; @@ -240,7 +241,11 @@ extern void nfs4_kill_renewd(struct nfs_client *); extern void nfs4_renew_state(struct work_struct *); /* nfs4state.c */ +struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp); struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp); +#if defined(CONFIG_NFS_V4_1) +struct rpc_cred *nfs4_get_machine_cred_locked(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 *); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index da481bda44f..f862a66e5fb 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4236,6 +4236,7 @@ static int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred) dprintk("--> %s\n", __func__); BUG_ON(clp == NULL); + p = (u32 *)verifier.data; *p++ = htonl((u32)clp->cl_boot_time.tv_sec); *p = htonl((u32)clp->cl_boot_time.tv_nsec); @@ -4795,12 +4796,14 @@ struct nfs4_state_recovery_ops nfs4_nograce_recovery_ops = { struct nfs4_state_maintenance_ops nfs40_state_renewal_ops = { .sched_state_renewal = nfs4_proc_async_renew, + .get_state_renewal_cred_locked = nfs4_get_renew_cred_locked, .renew_lease = nfs4_proc_renew, }; #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_locked = nfs4_get_machine_cred_locked, .renew_lease = nfs4_proc_sequence, }; #endif diff --git a/fs/nfs/nfs4renewd.c b/fs/nfs/nfs4renewd.c index 92b43e87267..e27c6cef18f 100644 --- a/fs/nfs/nfs4renewd.c +++ b/fs/nfs/nfs4renewd.c @@ -78,7 +78,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_locked(clp); + cred = ops->get_state_renewal_cred_locked(clp); spin_unlock(&clp->cl_lock); if (cred == NULL) { if (list_empty(&clp->cl_delegations)) { diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index dc5599c7f4d..da940abcfaa 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -77,7 +77,7 @@ static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred) return status; } -static struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp) +struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp) { struct rpc_cred *cred = NULL; @@ -114,17 +114,7 @@ struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp) return cred; } -static struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp) -{ - struct rpc_cred *cred; - - spin_lock(&clp->cl_lock); - cred = nfs4_get_renew_cred_locked(clp); - spin_unlock(&clp->cl_lock); - 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; @@ -1090,7 +1080,9 @@ static int nfs4_check_lease(struct nfs_client *clp) /* Is the client already known to have an expired lease? */ if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) return 0; - cred = nfs4_get_renew_cred(clp); + spin_lock(&clp->cl_lock); + cred = ops->get_state_renewal_cred_locked(clp); + spin_unlock(&clp->cl_lock); if (cred == NULL) { cred = nfs4_get_setclientid_cred(clp); if (cred == NULL) -- cgit v1.2.3 From 591d71cbde186cc498c0d9718dc17f2fadf7c643 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:47 -0400 Subject: nfs41: establish sessions-based clientid 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 >From 2.6.26 reclaimer() uses machine cred for setting up the client id therefore it is never expected to be NULL. Signed-off-by: Rahul Iyer [removed dprintk] Signed-off-by: Benny Halevy [nfs41: lease renewal] [revamped patch for new nfs4_state_manager design] Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 6 ++++-- fs/nfs/nfs4proc.c | 40 ++++++++++++++++++++++++++++++++++++++-- fs/nfs/nfs4state.c | 12 ++++++++---- 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 2b141c5758e..df9e3631949 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -178,6 +178,7 @@ struct nfs4_state_recovery_ops { int state_flag_bit; int (*recover_open)(struct nfs4_state_owner *, struct nfs4_state *); int (*recover_lock)(struct nfs4_state *, struct file_lock *); + int (*establish_clid)(struct nfs_client *, struct rpc_cred *); }; struct nfs4_state_maintenance_ops { @@ -200,6 +201,7 @@ extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struc 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_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 *); @@ -207,8 +209,8 @@ extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fh extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, struct nfs4_fs_locations *fs_locations, struct page *page); -extern struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops; -extern struct nfs4_state_recovery_ops nfs4_nograce_recovery_ops; +extern struct nfs4_state_recovery_ops *nfs4_reboot_recovery_ops[]; +extern struct nfs4_state_recovery_ops *nfs4_nograce_recovery_ops[]; #if defined(CONFIG_NFS_V4_1) extern int nfs4_setup_sequence(struct nfs_client *clp, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index f862a66e5fb..dd46339095c 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4780,19 +4780,41 @@ static int nfs41_proc_async_sequence(struct nfs_client *clp, #endif /* CONFIG_NFS_V4_1 */ -struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops = { +struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = { .owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT, .state_flag_bit = NFS_STATE_RECLAIM_REBOOT, .recover_open = nfs4_open_reclaim, .recover_lock = nfs4_lock_reclaim, + .establish_clid = nfs4_init_clientid, }; -struct nfs4_state_recovery_ops nfs4_nograce_recovery_ops = { +#if defined(CONFIG_NFS_V4_1) +struct nfs4_state_recovery_ops nfs41_reboot_recovery_ops = { + .owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT, + .state_flag_bit = NFS_STATE_RECLAIM_REBOOT, + .recover_open = nfs4_open_reclaim, + .recover_lock = nfs4_lock_reclaim, + .establish_clid = nfs4_proc_exchange_id, +}; +#endif /* CONFIG_NFS_V4_1 */ + +struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops = { + .owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE, + .state_flag_bit = NFS_STATE_RECLAIM_NOGRACE, + .recover_open = nfs4_open_expired, + .recover_lock = nfs4_lock_expired, + .establish_clid = nfs4_init_clientid, +}; + +#if defined(CONFIG_NFS_V4_1) +struct nfs4_state_recovery_ops nfs41_nograce_recovery_ops = { .owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE, .state_flag_bit = NFS_STATE_RECLAIM_NOGRACE, .recover_open = nfs4_open_expired, .recover_lock = nfs4_lock_expired, + .establish_clid = nfs4_proc_exchange_id, }; +#endif /* CONFIG_NFS_V4_1 */ struct nfs4_state_maintenance_ops nfs40_state_renewal_ops = { .sched_state_renewal = nfs4_proc_async_renew, @@ -4812,6 +4834,20 @@ struct nfs4_state_maintenance_ops nfs41_state_renewal_ops = { * Per minor version reboot and network partition recovery ops */ +struct nfs4_state_recovery_ops *nfs4_reboot_recovery_ops[] = { + &nfs40_reboot_recovery_ops, +#if defined(CONFIG_NFS_V4_1) + &nfs41_reboot_recovery_ops, +#endif +}; + +struct nfs4_state_recovery_ops *nfs4_nograce_recovery_ops[] = { + &nfs40_nograce_recovery_ops, +#if defined(CONFIG_NFS_V4_1) + &nfs41_nograce_recovery_ops, +#endif +}; + struct nfs4_state_maintenance_ops *nfs4_state_renewal_ops[] = { &nfs40_state_renewal_ops, #if defined(CONFIG_NFS_V4_1) diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index da940abcfaa..e17bd441217 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -60,7 +60,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) { unsigned short port; int status; @@ -1098,11 +1098,13 @@ out: static int nfs4_reclaim_lease(struct nfs_client *clp) { struct rpc_cred *cred; + struct nfs4_state_recovery_ops *ops = + nfs4_reboot_recovery_ops[clp->cl_minorversion]; int status = -ENOENT; cred = nfs4_get_setclientid_cred(clp); 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) { @@ -1208,7 +1210,8 @@ static void nfs4_state_manager(struct nfs_client *clp) } /* First recover reboot state... */ if (test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) { - status = nfs4_do_reclaim(clp, &nfs4_reboot_recovery_ops); + status = nfs4_do_reclaim(clp, + nfs4_reboot_recovery_ops[clp->cl_minorversion]); if (status == -NFS4ERR_STALE_CLIENTID) continue; if (test_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state)) @@ -1219,7 +1222,8 @@ static void nfs4_state_manager(struct nfs_client *clp) /* Now recover expired state... */ if (test_and_clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) { - status = nfs4_do_reclaim(clp, &nfs4_nograce_recovery_ops); + status = nfs4_do_reclaim(clp, + nfs4_nograce_recovery_ops[clp->cl_minorversion]); if (status < 0) { set_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state); if (status == -NFS4ERR_STALE_CLIENTID) -- cgit v1.2.3 From 90a16617ee6a052c3a1aac00eb67136324cf4dd0 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:48 -0400 Subject: nfs41: add a get_clid_cred function to nfs4_state_recovery_ops EXCHANGE_ID has different credential requirements than SETCLIENTID. Prepare for a separate credential function. Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 1 + fs/nfs/nfs4proc.c | 2 ++ fs/nfs/nfs4state.c | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index df9e3631949..1da3e354de2 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -179,6 +179,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 (*establish_clid)(struct nfs_client *, struct rpc_cred *); + struct rpc_cred * (*get_clid_cred)(struct nfs_client *); }; struct nfs4_state_maintenance_ops { diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index dd46339095c..b4e5442efa6 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4786,6 +4786,7 @@ struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = { .recover_open = nfs4_open_reclaim, .recover_lock = nfs4_lock_reclaim, .establish_clid = nfs4_init_clientid, + .get_clid_cred = nfs4_get_setclientid_cred, }; #if defined(CONFIG_NFS_V4_1) @@ -4804,6 +4805,7 @@ struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops = { .recover_open = nfs4_open_expired, .recover_lock = nfs4_lock_expired, .establish_clid = nfs4_init_clientid, + .get_clid_cred = nfs4_get_setclientid_cred, }; #if defined(CONFIG_NFS_V4_1) diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index e17bd441217..90e56072844 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1102,7 +1102,7 @@ static int nfs4_reclaim_lease(struct nfs_client *clp) nfs4_reboot_recovery_ops[clp->cl_minorversion]; int status = -ENOENT; - cred = nfs4_get_setclientid_cred(clp); + cred = ops->get_clid_cred(clp); if (cred != NULL) { status = ops->establish_clid(clp, cred); put_rpccred(cred); -- cgit v1.2.3 From b4b82607ffcf09b57301846d154f2c09c0b807c0 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:22:49 -0400 Subject: nfs41: get_clid_cred for EXCHANGE_ID Unlike SETCLIENTID, EXCHANGE_ID requires a machine credential. Do not search for credentials other than the machine credential. Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 1 + fs/nfs/nfs4proc.c | 2 ++ fs/nfs/nfs4state.c | 14 ++++++++++++++ 3 files changed, 17 insertions(+) diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 1da3e354de2..61bc3a32e1e 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -248,6 +248,7 @@ struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp); struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp); #if defined(CONFIG_NFS_V4_1) struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp); +struct rpc_cred *nfs4_get_exchange_id_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 *); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index b4e5442efa6..64d611658d1 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4796,6 +4796,7 @@ struct nfs4_state_recovery_ops nfs41_reboot_recovery_ops = { .recover_open = nfs4_open_reclaim, .recover_lock = nfs4_lock_reclaim, .establish_clid = nfs4_proc_exchange_id, + .get_clid_cred = nfs4_get_exchange_id_cred, }; #endif /* CONFIG_NFS_V4_1 */ @@ -4815,6 +4816,7 @@ struct nfs4_state_recovery_ops nfs41_nograce_recovery_ops = { .recover_open = nfs4_open_expired, .recover_lock = nfs4_lock_expired, .establish_clid = nfs4_proc_exchange_id, + .get_clid_cred = nfs4_get_exchange_id_cred, }; #endif /* CONFIG_NFS_V4_1 */ diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 90e56072844..7dc971335ec 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -114,6 +114,20 @@ struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp) return cred; } +#if defined(CONFIG_NFS_V4_1) + +struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp) +{ + struct rpc_cred *cred; + + spin_lock(&clp->cl_lock); + cred = nfs4_get_machine_cred_locked(clp); + spin_unlock(&clp->cl_lock); + return cred; +} + +#endif /* CONFIG_NFS_V4_1 */ + struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp) { struct nfs4_state_owner *sp; -- cgit v1.2.3 From 008f55d0e019943323c20a03493a2ba5672a4cc8 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:22:50 -0400 Subject: nfs41: recover lease in _nfs4_lookup_root This creates the nfsv4.1 session on mount. Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 16 ++++++++++++++++ fs/nfs/internal.h | 1 + fs/nfs/nfs4proc.c | 9 ++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index d28a987f569..3e232bf56df 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -527,6 +527,22 @@ void nfs_mark_client_ready(struct nfs_client *clp, int state) wake_up_all(&nfs_client_active_wq); } +/* + * With sessions, the client is not marked ready until after a + * successful EXCHANGE_ID and CREATE_SESSION. + * + * Map errors cl_cons_state errors to EPROTONOSUPPORT to indicate + * other versions of NFS can be tried. + */ +int nfs4_check_client_ready(struct nfs_client *clp) +{ + if (!nfs4_has_session(clp)) + return 0; + if (clp->cl_cons_state < NFS_CS_READY) + return -EPROTONOSUPPORT; + return 0; +} + /* * Initialise the timeout values for a connection */ diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 78508f29c03..acee3274d27 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -114,6 +114,7 @@ extern struct nfs_server *nfs_clone_server(struct nfs_server *, struct nfs_fh *, struct nfs_fattr *); extern void nfs_mark_client_ready(struct nfs_client *clp, int state); +extern int nfs4_check_client_ready(struct nfs_client *clp); #ifdef CONFIG_PROC_FS extern int __init nfs_fs_proc_init(void); extern void nfs_fs_proc_exit(void); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 64d611658d1..31ce758d53d 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2024,8 +2024,15 @@ static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle, .rpc_argp = &args, .rpc_resp = &res, }; + int status; + nfs_fattr_init(info->fattr); - return nfs4_call_sync(server, &msg, &args, &res, 0); + status = nfs4_recover_expired_lease(server); + if (!status) + status = nfs4_check_client_ready(server->nfs_client); + if (!status) + status = nfs4_call_sync(server, &msg, &args, &res, 0); + return status; } static int nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle, -- cgit v1.2.3 From 78722e9c9208a312695178f5331511badd190598 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Tue, 16 Jun 2009 14:43:15 -0400 Subject: nfs41: only retry EXCHANGE_ID on recoverable errors Stops an infinite loop of EXCHANGE_ID. Signed-off-by: Andy Adamson [fixed checkpatch warnings] Signed-off-by: Benny Halevy --- fs/nfs/nfs4state.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 7dc971335ec..2cc0aca96eb 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1183,6 +1183,27 @@ static int nfs4_reset_session(struct nfs_client *clp) { return 0; } static int nfs4_initialize_session(struct nfs_client *clp) { return 0; } #endif /* CONFIG_NFS_V4_1 */ +/* Set NFS4CLNT_LEASE_EXPIRED for all v4.0 errors and for recoverable errors + * on EXCHANGE_ID for v4.1 + */ +static void nfs4_set_lease_expired(struct nfs_client *clp, int status) +{ + if (nfs4_has_session(clp)) { + switch (status) { + case -NFS4ERR_DELAY: + case -NFS4ERR_CLID_INUSE: + case -EAGAIN: + break; + + case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery + * in nfs4_exchange_id */ + default: + return; + } + } + set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); +} + static void nfs4_state_manager(struct nfs_client *clp) { int status = 0; @@ -1193,7 +1214,7 @@ static void nfs4_state_manager(struct nfs_client *clp) /* We're going to have to re-establish a clientid */ status = nfs4_reclaim_lease(clp); if (status) { - set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); + nfs4_set_lease_expired(clp, status); if (status == -EAGAIN) continue; if (clp->cl_cons_state == -- cgit v1.2.3 From 34dc1ad752ad3f55b2a6e6cd8cfcf3504682fec7 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:22:52 -0400 Subject: nfs41: increment_{open,lock}_seqid 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 Signed-off-by: Rahul Iyer Signed-off-by: Benny Halevy [nfs41: check for session not minorversion] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 2 ++ fs/nfs/nfs4state.c | 12 +++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 31ce758d53d..f1f087b483c 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1808,6 +1808,8 @@ 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 (nfs4_has_session(server->nfs_client)) + 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) diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 2cc0aca96eb..2cfca9929c9 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -742,12 +742,14 @@ 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 (!nfs4_has_session(server->nfs_client)) + nfs_increment_seqid(status, seqid); } /* -- cgit v1.2.3 From 18dca02aeb3c49dfce87c76be643b139d05cf647 Mon Sep 17 00:00:00 2001 From: Ricardo Labiaga Date: Wed, 1 Apr 2009 09:22:53 -0400 Subject: nfs41: Add ability to read RPC call direction on TCP stream. 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. [nfs41: Keep track of RPC call/reply direction with a flag] As per 11/14/08 review of RFC 53/85. Add a new flag to track whether the incoming message is an RPC call or an RPC reply. TCP_RPC_REPLY is set in the 'struct sock_xprt' tcp_flags in xs_tcp_read_calldir() if the message is an RPC reply sent on the forechannel. It is cleared if the message is an RPC request sent on the back channel. Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- net/sunrpc/xprtsock.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index e1859614601..8975c10591c 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -270,6 +270,12 @@ 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) + +/* + * TCP RPC flags + */ +#define TCP_RPC_REPLY (1UL << 5) static inline struct sockaddr *xs_addr(struct rpc_xprt *xprt) { @@ -956,7 +962,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; @@ -991,13 +997,48 @@ 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_flags & TCP_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; + __be32 calldir; + + /* + * 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(calldir) - offset; + dprintk("RPC: reading CALL/REPLY flag (%Zu bytes)\n", len); + used = xdr_skb_read_bits(desc, &calldir, 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; + if (ntohl(calldir) == RPC_REPLY) + transport->tcp_flags |= TCP_RPC_REPLY; + else + transport->tcp_flags &= ~TCP_RPC_REPLY; + dprintk("RPC: reading %s CALL/REPLY flag %08x\n", + (transport->tcp_flags & TCP_RPC_REPLY) ? + "reply for" : "request with", 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); @@ -1114,6 +1155,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); -- cgit v1.2.3 From f4a2e418bfd03a1f25f515e8a92ecd584d96cfc1 Mon Sep 17 00:00:00 2001 From: Ricardo Labiaga Date: Wed, 1 Apr 2009 09:22:54 -0400 Subject: nfs41: Process the RPC call direction Reading and storing the RPC direction is a three step process. 1. xs_tcp_read_calldir() reads the RPC direction, but it will not store it in the XDR buffer since the 'struct rpc_rqst' is not yet available. 2. The 'struct rpc_rqst' is obtained during the TCP_RCV_COPY_DATA state. This state need not necessarily be preceeded by the TCP_RCV_READ_CALLDIR. For example, we may be reading a continuation packet to a large reply. Therefore, we can't simply obtain the 'struct rpc_rqst' during the TCP_RCV_READ_CALLDIR state and assume it's available during TCP_RCV_COPY_DATA. This patch adds a new TCP_RCV_READ_CALLDIR flag to indicate the need to read the RPC direction. It then uses TCP_RCV_COPY_CALLDIR to indicate the RPC direction needs to be saved after the 'struct rpc_rqst' has been allocated. 3. The 'struct rpc_rqst' is obtained by the xs_tcp_read_data() helper functions. xs_tcp_read_common() then saves the RPC direction in the XDR buffer if TCP_RCV_COPY_CALLDIR is set. This will happen when we're reading the data immediately after the direction was read. xs_tcp_read_common() then clears this flag. [was nfs41: Skip past the RPC call direction] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy [nfs41: sunrpc: Add RPC direction back into the XDR buffer] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy [nfs41: sunrpc: Don't skip past the RPC call direction] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- net/sunrpc/clnt.c | 5 +++-- net/sunrpc/xprtsock.c | 31 +++++++++++++++++++++++++------ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index d00e8135f86..aca3ab6fc14 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -1390,13 +1390,14 @@ rpc_verify_header(struct rpc_task *task) } if ((len -= 3) < 0) goto out_overflow; - p += 1; /* skip XID */ + 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); + task->tk_pid, __func__, n); goto out_garbage; } + if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) { if (--len < 0) goto out_overflow; diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 8975c10591c..a48df1449ec 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -270,12 +270,13 @@ 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) +#define TCP_RCV_READ_CALLDIR (1UL << 4) +#define TCP_RCV_COPY_CALLDIR (1UL << 5) /* * TCP RPC flags */ -#define TCP_RPC_REPLY (1UL << 5) +#define TCP_RPC_REPLY (1UL << 6) static inline struct sockaddr *xs_addr(struct rpc_xprt *xprt) { @@ -997,7 +998,7 @@ 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_CALLDIR; + transport->tcp_flags |= TCP_RCV_READ_CALLDIR; transport->tcp_copied = 4; dprintk("RPC: reading %s XID %08x\n", (transport->tcp_flags & TCP_RPC_REPLY) ? "reply for" @@ -1026,9 +1027,13 @@ static inline void xs_tcp_read_calldir(struct sock_xprt *transport, transport->tcp_offset += used; if (used != len) return; - transport->tcp_flags &= ~TCP_RCV_COPY_CALLDIR; + transport->tcp_flags &= ~TCP_RCV_READ_CALLDIR; + transport->tcp_flags |= TCP_RCV_COPY_CALLDIR; transport->tcp_flags |= TCP_RCV_COPY_DATA; - transport->tcp_copied += 4; + /* + * We don't yet have the XDR buffer, so we will write the calldir + * out after we get the buffer from the 'struct rpc_rqst' + */ if (ntohl(calldir) == RPC_REPLY) transport->tcp_flags |= TCP_RPC_REPLY; else @@ -1059,6 +1064,20 @@ static inline void xs_tcp_read_request(struct rpc_xprt *xprt, struct xdr_skb_rea } rcvbuf = &req->rq_private_buf; + + if (transport->tcp_flags & TCP_RCV_COPY_CALLDIR) { + /* + * Save the RPC direction in the XDR buffer + */ + __be32 calldir = transport->tcp_flags & TCP_RPC_REPLY ? + htonl(RPC_REPLY) : 0; + + memcpy(rcvbuf->head[0].iov_base + transport->tcp_copied, + &calldir, sizeof(calldir)); + transport->tcp_copied += sizeof(calldir); + transport->tcp_flags &= ~TCP_RCV_COPY_CALLDIR; + } + len = desc->count; if (len > transport->tcp_reclen - transport->tcp_offset) { struct xdr_skb_reader my_desc; @@ -1156,7 +1175,7 @@ static int xs_tcp_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, uns continue; } /* Read in the call/reply flag */ - if (transport->tcp_flags & TCP_RCV_COPY_CALLDIR) { + if (transport->tcp_flags & TCP_RCV_READ_CALLDIR) { xs_tcp_read_calldir(transport, &desc); continue; } -- cgit v1.2.3 From 9bdaa86d2a4cbf9a71de3048c0c0a874e7ebc8ad Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:22:55 -0400 Subject: nfs41: Refactor nfs4_{init,destroy}_callback for nfs4.0 Refactor-out code to bring the callback service up and down. Signed-off-by: Benny Halevy --- fs/nfs/client.c | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 3e232bf56df..d9657d45573 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -47,6 +47,9 @@ #include "internal.h" #include "fscache.h" +static int nfs4_init_callback(struct nfs_client *); +static void nfs4_destroy_callback(struct nfs_client *); + #define NFSDBG_FACILITY NFSDBG_CLIENT static DEFINE_SPINLOCK(nfs_client_lock); @@ -121,11 +124,8 @@ 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); - } + if (nfs4_init_callback(clp) < 0) + goto error_2; atomic_set(&clp->cl_count, 1); clp->cl_cons_state = NFS_CS_INITING; @@ -162,8 +162,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(); + nfs4_destroy_callback(clp); error_2: kfree(clp); error_0: @@ -183,6 +182,17 @@ static void nfs4_shutdown_client(struct nfs_client *clp) #endif } +/* + * Destroy the NFS4 callback service + */ +static void nfs4_destroy_callback(struct nfs_client *clp) +{ +#ifdef CONFIG_NFS_V4 + if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) + nfs_callback_down(); +#endif /* CONFIG_NFS_V4 */ +} + /* * Clears/puts all minor version specific parts from an nfs_client struct * reverting it to minorversion 0. @@ -215,8 +225,7 @@ static void nfs_free_client(struct nfs_client *clp) if (!IS_ERR(clp->cl_rpcclient)) rpc_shutdown_client(clp->cl_rpcclient); - if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) - nfs_callback_down(); + nfs4_destroy_callback(clp); if (clp->cl_machine_cred != NULL) put_rpccred(clp->cl_machine_cred); @@ -1087,6 +1096,25 @@ error: } #ifdef CONFIG_NFS_V4 +/* + * Initialize the NFS4 callback service + */ +static int nfs4_init_callback(struct nfs_client *clp) +{ + int error; + + if (clp->rpc_ops->version == 4) { + error = nfs_callback_up(); + if (error < 0) { + dprintk("%s: failed to start callback. Error = %d\n", + __func__, error); + return error; + } + __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state); + } + return 0; +} + /* * Initialize the minor version specific parts of an NFS4 client record */ -- cgit v1.2.3 From 7146851376861fe55c7a48ac8fc1354a5fff6cd0 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:22:56 -0400 Subject: nfs41: minorversion support for nfs4_{init,destroy}_callback move nfs4_init_callback into nfs4_init_client_minor_version and nfs4_destroy_callback into nfs4_clear_client_minor_version as these need to happen also when auto-negotiating the minorversion once the callback service for nfs41 becomes different than for nfs4.0 Signed-off-by: Benny Halevy [nfs41: Fix checkpatch warning] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy [Type check arguments of nfs_callback_up] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy [nfs41: Backchannel: Remove FIXME comment] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- fs/nfs/callback.c | 65 +++++++++++++++++++++++++++++++++++++++---------------- fs/nfs/callback.h | 2 +- fs/nfs/client.c | 21 ++++++------------ 3 files changed, 54 insertions(+), 34 deletions(-) diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index a886e692ddd..4b1313eda6f 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -59,7 +59,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; @@ -97,20 +97,12 @@ nfs_callback_svc(void *vrqstp) } /* - * Bring up the callback thread if it is not already up. + * Prepare to bring up the NFSv4 callback service */ -int nfs_callback_up(void) +struct svc_rqst * +nfs4_callback_up(struct svc_serv *serv) { - struct svc_serv *serv = NULL; - int ret = 0; - - mutex_lock(&nfs_callback_mutex); - if (nfs_callback_info.users++ || nfs_callback_info.task != NULL) - goto out; - serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL); - ret = -ENOMEM; - if (!serv) - goto out_err; + int ret; ret = svc_create_xprt(serv, "tcp", PF_INET, nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS); @@ -131,18 +123,53 @@ int nfs_callback_up(void) goto out_err; #endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ - 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; + return svc_prepare_thread(serv, &serv->sv_pools[0]); + +out_err: + if (ret == 0) + ret = -ENOMEM; + return ERR_PTR(ret); +} + +/* + * Bring up the callback thread if it is not already up. + */ +int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt) +{ + 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); + if (nfs_callback_info.users++ || nfs_callback_info.task != NULL) + goto out; + serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL); + if (!serv) { + ret = -ENOMEM; + goto out_err; + } + + 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); diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h index e110e286a26..9b907f408b8 100644 --- a/fs/nfs/callback.h +++ b/fs/nfs/callback.h @@ -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, struct rpc_xprt *xprt); extern void nfs_callback_down(void); #else #define nfs_callback_up() (0) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index d9657d45573..df2b40d1883 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -47,9 +47,6 @@ #include "internal.h" #include "fscache.h" -static int nfs4_init_callback(struct nfs_client *); -static void nfs4_destroy_callback(struct nfs_client *); - #define NFSDBG_FACILITY NFSDBG_CLIENT static DEFINE_SPINLOCK(nfs_client_lock); @@ -124,9 +121,6 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_ clp->rpc_ops = cl_init->rpc_ops; - if (nfs4_init_callback(clp) < 0) - goto error_2; - atomic_set(&clp->cl_count, 1); clp->cl_cons_state = NFS_CS_INITING; @@ -136,7 +130,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); @@ -161,9 +155,7 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_ return clp; -error_3: - nfs4_destroy_callback(clp); -error_2: +error_cleanup: kfree(clp); error_0: return NULL; @@ -207,6 +199,8 @@ static void nfs4_clear_client_minor_version(struct nfs_client *clp) clp->cl_call_sync = _nfs4_call_sync; #endif /* CONFIG_NFS_V4_1 */ + + nfs4_destroy_callback(clp); } /* @@ -225,8 +219,6 @@ static void nfs_free_client(struct nfs_client *clp) if (!IS_ERR(clp->cl_rpcclient)) rpc_shutdown_client(clp->cl_rpcclient); - nfs4_destroy_callback(clp); - if (clp->cl_machine_cred != NULL) put_rpccred(clp->cl_machine_cred); @@ -1104,7 +1096,8 @@ static int nfs4_init_callback(struct nfs_client *clp) int error; if (clp->rpc_ops->version == 4) { - error = nfs_callback_up(); + 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); @@ -1139,7 +1132,7 @@ static int nfs4_init_client_minor_version(struct nfs_client *clp) } #endif /* CONFIG_NFS_V4_1 */ - return 0; + return nfs4_init_callback(clp); } /* -- cgit v1.2.3 From 56632b5bff5af10eb12d7e9499b5ffcadcb7a7b2 Mon Sep 17 00:00:00 2001 From: Ricardo Labiaga Date: Wed, 1 Apr 2009 09:22:58 -0400 Subject: nfs41: client callback structures 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. 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. [nfs41: NFSv4.1 callback support] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- include/linux/sunrpc/svc.h | 8 ++++++++ include/linux/sunrpc/xprt.h | 22 ++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 2a30775959e..4a8afbd6200 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -96,6 +96,14 @@ struct svc_serv { svc_thread_fn sv_function; /* main function for threads */ unsigned int sv_drc_max_pages; /* Total pages for DRC */ unsigned int sv_drc_pages_used;/* DRC pages used */ +#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 */ }; /* diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 08afe43118f..703af7ebf6c 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -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 */ -- cgit v1.2.3 From f9acac1a4710ce88871f1ae323fc91c1cb6e9d52 Mon Sep 17 00:00:00 2001 From: Ricardo Labiaga Date: Wed, 1 Apr 2009 09:22:59 -0400 Subject: nfs41: Initialize new rpc_xprt callback related fields Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- net/sunrpc/xprt.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 06ca058572f..52739f82df1 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -1049,6 +1049,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); -- cgit v1.2.3 From fb7a0b9addbdbbb13b7bc02abf55ee524ea19ce1 Mon Sep 17 00:00:00 2001 From: Ricardo Labiaga Date: Wed, 1 Apr 2009 09:23:00 -0400 Subject: nfs41: New backchannel helper routines 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. To be consistent with the reception of RPC messages, the backchannel requests should be placed into the 'struct rpc_rqst' rq_rcv_buf, which is then in turn copied to the 'struct rpc_rqst' rq_private_buf. [nfs41: Preallocate rpc_rqst receive buffer for handling callbacks] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy [Update copyright notice and explain page allocation] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- include/linux/sunrpc/xprt.h | 1 + net/sunrpc/Makefile | 1 + net/sunrpc/backchannel_rqst.c | 278 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 280 insertions(+) create mode 100644 net/sunrpc/backchannel_rqst.c diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 703af7ebf6c..beae030e80b 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -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 diff --git a/net/sunrpc/Makefile b/net/sunrpc/Makefile index 5369aa369b3..4a01f9684b8 100644 --- a/net/sunrpc/Makefile +++ b/net/sunrpc/Makefile @@ -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 diff --git a/net/sunrpc/backchannel_rqst.c b/net/sunrpc/backchannel_rqst.c new file mode 100644 index 00000000000..f56e18a2349 --- /dev/null +++ b/net/sunrpc/backchannel_rqst.c @@ -0,0 +1,278 @@ +/****************************************************************************** + +(c) 2007 Network Appliance, Inc. All Rights Reserved. +(c) 2009 NetApp. All Rights Reserved. + +NetApp 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. + +******************************************************************************/ + +#include +#include + +#ifdef RPC_DEBUG +#define RPCDBG_FACILITY RPCDBG_TRANS +#endif + +#if defined(CONFIG_NFS_V4_1) + +/* + * Helper routines that track the number of preallocation elements + * on the transport. + */ +static inline int xprt_need_to_requeue(struct rpc_xprt *xprt) +{ + return xprt->bc_alloc_count > 0; +} + +static inline void xprt_inc_alloc_count(struct rpc_xprt *xprt, unsigned int n) +{ + xprt->bc_alloc_count += n; +} + +static inline int xprt_dec_alloc_count(struct rpc_xprt *xprt, unsigned int n) +{ + return xprt->bc_alloc_count -= n; +} + +/* + * Free the preallocated rpc_rqst structure and the memory + * buffers hanging off of it. + */ +static void xprt_free_allocation(struct rpc_rqst *req) +{ + struct xdr_buf *xbufp; + + dprintk("RPC: free allocations for req= %p\n", req); + BUG_ON(test_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state)); + xbufp = &req->rq_private_buf; + free_page((unsigned long)xbufp->head[0].iov_base); + xbufp = &req->rq_snd_buf; + free_page((unsigned long)xbufp->head[0].iov_base); + list_del(&req->rq_bc_pa_list); + kfree(req); +} + +/* + * Preallocate up to min_reqs structures and related buffers for use + * by the backchannel. This function can be called multiple times + * when creating new sessions that use the same rpc_xprt. The + * preallocated buffers are added to the pool of resources used by + * the rpc_xprt. Anyone of these resources may be used used by an + * incoming callback request. It's up to the higher levels in the + * stack to enforce that the maximum number of session slots is not + * being exceeded. + * + * Some callback arguments can be large. For example, a pNFS server + * using multiple deviceids. The list can be unbound, but the client + * has the ability to tell the server the maximum size of the callback + * requests. Each deviceID is 16 bytes, so allocate one page + * for the arguments to have enough room to receive a number of these + * deviceIDs. The NFS client indicates to the pNFS server that its + * callback requests can be up to 4096 bytes in size. + */ +int xprt_setup_backchannel(struct rpc_xprt *xprt, unsigned int min_reqs) +{ + struct page *page_rcv = NULL, *page_snd = NULL; + struct xdr_buf *xbufp = NULL; + struct rpc_rqst *req, *tmp; + struct list_head tmp_list; + int i; + + dprintk("RPC: setup backchannel transport\n"); + + /* + * We use a temporary list to keep track of the preallocated + * buffers. Once we're done building the list we splice it + * into the backchannel preallocation list off of the rpc_xprt + * struct. This helps minimize the amount of time the list + * lock is held on the rpc_xprt struct. It also makes cleanup + * easier in case of memory allocation errors. + */ + INIT_LIST_HEAD(&tmp_list); + for (i = 0; i < min_reqs; i++) { + /* Pre-allocate one backchannel rpc_rqst */ + req = kzalloc(sizeof(struct rpc_rqst), GFP_KERNEL); + if (req == NULL) { + printk(KERN_ERR "Failed to create bc rpc_rqst\n"); + goto out_free; + } + + /* Add the allocated buffer to the tmp list */ + dprintk("RPC: adding req= %p\n", req); + list_add(&req->rq_bc_pa_list, &tmp_list); + + req->rq_xprt = xprt; + INIT_LIST_HEAD(&req->rq_list); + INIT_LIST_HEAD(&req->rq_bc_list); + + /* Preallocate one XDR receive buffer */ + page_rcv = alloc_page(GFP_KERNEL); + if (page_rcv == NULL) { + printk(KERN_ERR "Failed to create bc receive xbuf\n"); + goto out_free; + } + xbufp = &req->rq_rcv_buf; + xbufp->head[0].iov_base = page_address(page_rcv); + xbufp->head[0].iov_len = PAGE_SIZE; + xbufp->tail[0].iov_base = NULL; + xbufp->tail[0].iov_len = 0; + xbufp->page_len = 0; + xbufp->len = PAGE_SIZE; + xbufp->buflen = PAGE_SIZE; + + /* Preallocate one XDR send buffer */ + page_snd = alloc_page(GFP_KERNEL); + if (page_snd == NULL) { + printk(KERN_ERR "Failed to create bc snd xbuf\n"); + goto out_free; + } + + xbufp = &req->rq_snd_buf; + xbufp->head[0].iov_base = page_address(page_snd); + xbufp->head[0].iov_len = 0; + xbufp->tail[0].iov_base = NULL; + xbufp->tail[0].iov_len = 0; + xbufp->page_len = 0; + xbufp->len = 0; + xbufp->buflen = PAGE_SIZE; + } + + /* + * Add the temporary list to the backchannel preallocation list + */ + spin_lock_bh(&xprt->bc_pa_lock); + list_splice(&tmp_list, &xprt->bc_pa_list); + xprt_inc_alloc_count(xprt, min_reqs); + spin_unlock_bh(&xprt->bc_pa_lock); + + dprintk("RPC: setup backchannel transport done\n"); + return 0; + +out_free: + /* + * Memory allocation failed, free the temporary list + */ + list_for_each_entry_safe(req, tmp, &tmp_list, rq_bc_pa_list) + xprt_free_allocation(req); + + dprintk("RPC: setup backchannel transport failed\n"); + return -1; +} +EXPORT_SYMBOL(xprt_setup_backchannel); + +/* + * Destroys the backchannel preallocated structures. + * Since these structures may have been allocated by multiple calls + * to xprt_setup_backchannel, we only destroy up to the maximum number + * of reqs specified by the caller. + * @xprt: the transport holding the preallocated strucures + * @max_reqs the maximum number of preallocated structures to destroy + */ +void xprt_destroy_backchannel(struct rpc_xprt *xprt, unsigned int max_reqs) +{ + struct rpc_rqst *req = NULL, *tmp = NULL; + + dprintk("RPC: destroy backchannel transport\n"); + + BUG_ON(max_reqs == 0); + spin_lock_bh(&xprt->bc_pa_lock); + xprt_dec_alloc_count(xprt, max_reqs); + list_for_each_entry_safe(req, tmp, &xprt->bc_pa_list, rq_bc_pa_list) { + dprintk("RPC: req=%p\n", req); + xprt_free_allocation(req); + if (--max_reqs == 0) + break; + } + spin_unlock_bh(&xprt->bc_pa_lock); + + dprintk("RPC: backchannel list empty= %s\n", + list_empty(&xprt->bc_pa_list) ? "true" : "false"); +} +EXPORT_SYMBOL(xprt_destroy_backchannel); + +/* + * One or more rpc_rqst structure have been preallocated during the + * backchannel setup. Buffer space for the send and private XDR buffers + * has been preallocated as well. Use xprt_alloc_bc_request to allocate + * to this request. Use xprt_free_bc_request to return it. + * + * Return an available rpc_rqst, otherwise NULL if non are available. + */ +struct rpc_rqst *xprt_alloc_bc_request(struct rpc_xprt *xprt) +{ + struct rpc_rqst *req; + + dprintk("RPC: allocate a backchannel request\n"); + spin_lock_bh(&xprt->bc_pa_lock); + if (!list_empty(&xprt->bc_pa_list)) { + req = list_first_entry(&xprt->bc_pa_list, struct rpc_rqst, + rq_bc_pa_list); + list_del(&req->rq_bc_pa_list); + } else { + req = NULL; + } + spin_unlock_bh(&xprt->bc_pa_lock); + + if (req != NULL) { + set_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state); + req->rq_received = 0; + req->rq_bytes_sent = 0; + memcpy(&req->rq_private_buf, &req->rq_rcv_buf, + sizeof(req->rq_private_buf)); + } + dprintk("RPC: backchannel req=%p\n", req); + return req; +} + +/* + * Return the preallocated rpc_rqst structure and XDR buffers + * associated with this rpc_task. + */ +void xprt_free_bc_request(struct rpc_rqst *req) +{ + struct rpc_xprt *xprt = req->rq_xprt; + + dprintk("RPC: free backchannel req=%p\n", req); + + smp_mb__before_clear_bit(); + BUG_ON(!test_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state)); + clear_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state); + smp_mb__after_clear_bit(); + + if (!xprt_need_to_requeue(xprt)) { + /* + * The last remaining session was destroyed while this + * entry was in use. Free the entry and don't attempt + * to add back to the list because there is no need to + * have anymore preallocated entries. + */ + dprintk("RPC: Last session removed req=%p\n", req); + xprt_free_allocation(req); + return; + } + + /* + * Return it to the list of preallocations so that it + * may be reused by a new callback request. + */ + spin_lock_bh(&xprt->bc_pa_lock); + list_add(&req->rq_bc_pa_list, &xprt->bc_pa_list); + spin_unlock_bh(&xprt->bc_pa_lock); +} + +#endif /* CONFIG_NFS_V4_1 */ -- cgit v1.2.3 From 4a8d70bfef01f8e6b27785e2625e88e9a80924a5 Mon Sep 17 00:00:00 2001 From: Ricardo Labiaga Date: Wed, 1 Apr 2009 09:23:01 -0400 Subject: nfs41: New include/linux/sunrpc/bc_xprt.h Contains prototype for backchannel helper routines. Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy [nfs41: xprt_setup_backchannel v4.0 only inline] Fix compile error when CONFIG_NFS_V4_1 is not set. Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy [Update Copyright notice and fix formatting] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- include/linux/sunrpc/bc_xprt.h | 46 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 include/linux/sunrpc/bc_xprt.h diff --git a/include/linux/sunrpc/bc_xprt.h b/include/linux/sunrpc/bc_xprt.h new file mode 100644 index 00000000000..5965ae4f902 --- /dev/null +++ b/include/linux/sunrpc/bc_xprt.h @@ -0,0 +1,46 @@ +/****************************************************************************** + +(c) 2008 NetApp. All Rights Reserved. + +NetApp 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 +#include + +#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); +#else /* CONFIG_NFS_V4_1 */ +static inline int xprt_setup_backchannel(struct rpc_xprt *xprt, + unsigned int min_reqs) +{ + return 0; +} +#endif /* CONFIG_NFS_V4_1 */ +#endif /* _LINUX_SUNRPC_BC_XPRT_H */ + -- cgit v1.2.3 From 44b98efdd0a205bdca2cb63493350d06ff6804b1 Mon Sep 17 00:00:00 2001 From: Ricardo Labiaga Date: Wed, 1 Apr 2009 09:23:02 -0400 Subject: nfs41: New xs_tcp_read_data() 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 (iyer@netapp.com) and Benny Halevy (bhalevy@panasas.com). xs_tcp_read_callback() drops the connection when the number of expected callbacks is exceeded. Use xprt_force_disconnect(), ensuring tasks on the pending queue are awaken on disconnect. [nfs41: Keep track of RPC call/reply direction with a flag] [nfs41: Preallocate rpc_rqst receive buffer for handling callbacks] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy [nfs41: sunrpc: xs_tcp_read_callback() should use xprt_force_disconnect()] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy [Moves embedded #ifdefs into #ifdef function blocks] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- net/sunrpc/xprtsock.c | 144 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 126 insertions(+), 18 deletions(-) diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index a48df1449ec..e3e3a57116f 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -34,6 +34,9 @@ #include #include #include +#ifdef CONFIG_NFS_V4_1 +#include +#endif #include #include @@ -1044,25 +1047,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; if (transport->tcp_flags & TCP_RCV_COPY_CALLDIR) { @@ -1114,7 +1108,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", @@ -1130,11 +1124,125 @@ 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) { + printk(KERN_WARNING "Callback slot table overflowed\n"); + xprt_force_disconnect(xprt); + 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); + } + + req->rq_private_buf.len = transport->tcp_copied; + + return 0; +} + +static inline int _xs_tcp_read_data(struct rpc_xprt *xprt, + struct xdr_skb_reader *desc) +{ + struct sock_xprt *transport = + container_of(xprt, struct sock_xprt, xprt); + + return (transport->tcp_flags & TCP_RPC_REPLY) ? + xs_tcp_read_reply(xprt, desc) : + xs_tcp_read_callback(xprt, desc); +} +#else +static inline int _xs_tcp_read_data(struct rpc_xprt *xprt, + struct xdr_skb_reader *desc) +{ + return xs_tcp_read_reply(xprt, desc); +} +#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); + + if (_xs_tcp_read_data(xprt, desc) == 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) @@ -1181,7 +1289,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 */ -- cgit v1.2.3 From 88b5ed73bcd0f21e008b6e303a02c8b7cb1199f4 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 17 Jun 2009 13:22:57 -0700 Subject: SUNRPC: Fix a missing "break" option in xs_tcp_setup_socket() In the case of -EADDRNOTAVAIL and/or unhandled connection errors, we want to get rid of the existing socket and retry immediately, just as the comment says. Currently we end up sleeping for a minute, due to the missing "break" statement. Signed-off-by: Trond Myklebust --- net/sunrpc/xprtsock.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 6c2d6158655..f7f3dfd211e 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -1792,6 +1792,7 @@ static void xs_tcp_setup_socket(struct rpc_xprt *xprt, */ set_bit(XPRT_CONNECTION_CLOSE, &xprt->state); xprt_force_disconnect(xprt); + break; case -ECONNREFUSED: case -ECONNRESET: case -ENETUNREACH: -- cgit v1.2.3 From 01c3f05228ce7fc19baa103e4e4bf6c1b5062a53 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 17 Jun 2009 13:22:58 -0700 Subject: NFSv4: Fix the 'nolock' option regression NFSv4 should just ignore the 'nolock' option. It is an NFSv2/v3 thing... This fixes the Oops in http://bugzilla.kernel.org/show_bug.cgi?id=13330 Signed-off-by: Trond Myklebust --- fs/nfs/super.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 26127b69a27..98b47d17740 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -2240,6 +2240,11 @@ static void nfs4_fill_super(struct super_block *sb) nfs_initialise_sb(sb); } +static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *args) +{ + args->flags &= ~(NFS_MOUNT_NONLM|NFS_MOUNT_NOACL|NFS_MOUNT_VER3); +} + /* * Validate NFSv4 mount options */ @@ -2336,6 +2341,8 @@ static int nfs4_validate_mount_data(void *options, nfs_validate_transport_protocol(args); + nfs4_validate_mount_flags(args); + if (args->auth_flavor_len > 1) goto out_inval_auth; -- cgit v1.2.3 From d5122201a7f90b2aa73092f158b84d1d74f1134d Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 17 Jun 2009 13:22:58 -0700 Subject: NFSv4: Move error handling out of the delegation generic code The NFSv4 delegation recovery code is required by the protocol to handle more errors. Rather than add NFSv4.0 specific errors into 'generic' delegation code, we should move the error handling into the NFSv4 layer. Signed-off-by: Trond Myklebust --- fs/nfs/delegation.c | 18 +++--------------- fs/nfs/nfs4proc.c | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 968225a8801..d4f669f0683 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -68,7 +68,7 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_ { struct inode *inode = state->inode; struct file_lock *fl; - int status; + int status = 0; for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK))) @@ -76,21 +76,9 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_ if (nfs_file_open_context(fl->fl_file) != ctx) continue; status = nfs4_lock_delegation_recall(state, fl); - if (status >= 0) - continue; - switch (status) { - default: - printk(KERN_ERR "%s: unhandled error %d.\n", - __func__, status); - case -NFS4ERR_EXPIRED: - /* kill_proc(fl->fl_pid, SIGLOST, 1); */ - case -NFS4ERR_STALE_CLIENTID: - nfs4_schedule_state_recovery(NFS_SERVER(inode)->nfs_client); - goto out_err; - } + if (status < 0) + break; } - return 0; -out_err: return status; } diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 4674f8092da..d326193c978 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3630,8 +3630,19 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl) goto out; do { err = _nfs4_do_setlk(state, F_SETLK, fl, 0); - if (err != -NFS4ERR_DELAY) - break; + switch (err) { + default: + printk(KERN_ERR "%s: unhandled error %d.\n", + __func__, err); + case 0: + goto out; + case -NFS4ERR_EXPIRED: + case -NFS4ERR_STALE_CLIENTID: + nfs4_schedule_state_recovery(server->nfs_client); + goto out; + case -NFS4ERR_DELAY: + break; + } err = nfs4_handle_exception(server, err, &exception); } while (exception.retry); out: -- cgit v1.2.3 From 965b5d679146c9f69bc0325388bb9ed357863c4f Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 17 Jun 2009 13:22:59 -0700 Subject: NFSv4: Handle more errors when recovering open file and locking state It is possible for servers to return NFS4ERR_BAD_STATEID when the state management code is recovering locks or is reclaiming state when returning a delegation. Ensure that we handle that case. While we're at it, add in handlers for NFS4ERR_STALE, NFS4ERR_ADMIN_REVOKED, NFS4ERR_OPENMODE, NFS4ERR_DENIED and NFS4ERR_STALE_STATEID, since the protocol appears to allow for them too. Also handle ENOMEM... Finally, rather than add new NFSv4.0-specific errors and error handling into the generic delegation code, move that open file and locking state error handling into the NFSv4 layer. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 44 ++++++++++++++++++++++++++++++++++++++------ fs/nfs/nfs4state.c | 28 ++++++++++++++++++---------- 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index d326193c978..55314e72163 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -804,16 +804,30 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state err = _nfs4_open_delegation_recall(ctx, state, stateid); switch (err) { case 0: - return err; + case -ENOENT: + case -ESTALE: + goto out; case -NFS4ERR_STALE_CLIENTID: case -NFS4ERR_STALE_STATEID: case -NFS4ERR_EXPIRED: /* Don't recall a delegation if it was lost */ nfs4_schedule_state_recovery(server->nfs_client); - return err; + goto out; + case -ERESTARTSYS: + /* + * The show must go on: exit, but mark the + * stateid as needing recovery. + */ + case -NFS4ERR_ADMIN_REVOKED: + case -NFS4ERR_BAD_STATEID: + nfs4_state_mark_reclaim_nograce(server->nfs_client, state); + case -ENOMEM: + err = 0; + goto out; } err = nfs4_handle_exception(server, err, &exception); } while (exception.retry); +out: return err; } @@ -3487,8 +3501,6 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f ret = nfs4_wait_for_completion_rpc_task(task); if (ret == 0) { ret = data->rpc_status; - if (ret == -NFS4ERR_DENIED) - ret = -EAGAIN; } else data->cancelled = 1; rpc_put_task(task); @@ -3576,9 +3588,11 @@ static int nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock * int err; do { + err = _nfs4_proc_setlk(state, cmd, request); + if (err == -NFS4ERR_DENIED) + err = -EAGAIN; err = nfs4_handle_exception(NFS_SERVER(state->inode), - _nfs4_proc_setlk(state, cmd, request), - &exception); + err, &exception); } while (exception.retry); return err; } @@ -3635,11 +3649,29 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl) printk(KERN_ERR "%s: unhandled error %d.\n", __func__, err); case 0: + case -ESTALE: goto out; case -NFS4ERR_EXPIRED: case -NFS4ERR_STALE_CLIENTID: + case -NFS4ERR_STALE_STATEID: nfs4_schedule_state_recovery(server->nfs_client); goto out; + case -ERESTARTSYS: + /* + * The show must go on: exit, but mark the + * stateid as needing recovery. + */ + case -NFS4ERR_ADMIN_REVOKED: + case -NFS4ERR_BAD_STATEID: + case -NFS4ERR_OPENMODE: + nfs4_state_mark_reclaim_nograce(server->nfs_client, state); + err = 0; + goto out; + case -ENOMEM: + case -NFS4ERR_DENIED: + /* kill_proc(fl->fl_pid, SIGLOST, 1); */ + err = 0; + goto out; case -NFS4ERR_DELAY: break; } diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 0298e909559..6e094e95cf0 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -854,25 +854,29 @@ static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_ if (nfs_file_open_context(fl->fl_file)->state != state) continue; status = ops->recover_lock(state, fl); - if (status >= 0) - continue; switch (status) { + case 0: + break; + case -ESTALE: + case -NFS4ERR_ADMIN_REVOKED: + case -NFS4ERR_STALE_STATEID: + case -NFS4ERR_BAD_STATEID: + case -NFS4ERR_EXPIRED: + case -NFS4ERR_NO_GRACE: + case -NFS4ERR_STALE_CLIENTID: + goto out; default: printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n", __func__, status); - case -NFS4ERR_EXPIRED: - case -NFS4ERR_NO_GRACE: + case -ENOMEM: + case -NFS4ERR_DENIED: case -NFS4ERR_RECLAIM_BAD: case -NFS4ERR_RECLAIM_CONFLICT: /* kill_proc(fl->fl_pid, SIGLOST, 1); */ - break; - case -NFS4ERR_STALE_CLIENTID: - goto out_err; + status = 0; } } - up_write(&nfsi->rwsem); - return 0; -out_err: +out: up_write(&nfsi->rwsem); return status; } @@ -918,6 +922,7 @@ restart: printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n", __func__, status); case -ENOENT: + case -ENOMEM: case -ESTALE: /* * Open state on this file cannot be recovered @@ -928,6 +933,9 @@ restart: /* Mark the file as being 'closed' */ state->state = 0; break; + case -NFS4ERR_ADMIN_REVOKED: + case -NFS4ERR_STALE_STATEID: + case -NFS4ERR_BAD_STATEID: case -NFS4ERR_RECLAIM_BAD: case -NFS4ERR_RECLAIM_CONFLICT: nfs4_state_mark_reclaim_nograce(sp->so_client, state); -- cgit v1.2.3 From 3f09df70e3a33590ae5a97b8a15486d3711c7065 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 17 Jun 2009 13:23:00 -0700 Subject: NFS: Ensure we always hold the BKL when dereferencing inode->i_flock Signed-off-by: Trond Myklebust --- fs/nfs/delegation.c | 16 ++++++++++++++-- fs/nfs/nfs4state.c | 9 +++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index d4f669f0683..af05b918cb5 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -70,15 +70,24 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_ struct file_lock *fl; int status = 0; + if (inode->i_flock == NULL) + goto out; + + /* Protect inode->i_flock using the BKL */ + lock_kernel(); for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK))) continue; if (nfs_file_open_context(fl->fl_file) != ctx) continue; + unlock_kernel(); status = nfs4_lock_delegation_recall(state, fl); if (status < 0) - break; + goto out; + lock_kernel(); } + unlock_kernel(); +out: return status; } @@ -256,7 +265,10 @@ static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegat struct nfs_inode *nfsi = NFS_I(inode); nfs_msync_inode(inode); - /* Guard against new delegated open calls */ + /* + * Guard against new delegated open/lock/unlock calls and against + * state recovery + */ down_write(&nfsi->rwsem); nfs_delegation_claim_opens(inode, &delegation->stateid); up_write(&nfsi->rwsem); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 6e094e95cf0..0b2a19bfe5a 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -847,12 +847,19 @@ static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_ struct file_lock *fl; int status = 0; + if (inode->i_flock == NULL) + return 0; + + /* Guard against delegation returns and new lock/unlock calls */ down_write(&nfsi->rwsem); + /* Protect inode->i_flock using the BKL */ + lock_kernel(); for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK))) continue; if (nfs_file_open_context(fl->fl_file)->state != state) continue; + unlock_kernel(); status = ops->recover_lock(state, fl); switch (status) { case 0: @@ -875,7 +882,9 @@ static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_ /* kill_proc(fl->fl_pid, SIGLOST, 1); */ status = 0; } + lock_kernel(); } + unlock_kernel(); out: up_write(&nfsi->rwsem); return status; -- cgit v1.2.3 From 5cd973c44a92f4abf8f7084c804089b3eaa7b4bf Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 17 Jun 2009 13:23:01 -0700 Subject: NFSv4/NLM: Push file locking BKL dependencies down into the NLM layer Signed-off-by: Trond Myklebust --- fs/lockd/clntproc.c | 2 ++ fs/nfs/file.c | 6 ------ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index dd7957064a8..273e229353f 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -165,6 +165,7 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl) /* Set up the argument struct */ nlmclnt_setlockargs(call, fl); + lock_kernel(); if (IS_SETLK(cmd) || IS_SETLKW(cmd)) { if (fl->fl_type != F_UNLCK) { call->a_args.block = IS_SETLKW(cmd) ? 1 : 0; @@ -178,6 +179,7 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl) fl->fl_ops->fl_release_private(fl); fl->fl_ops = NULL; + unlock_kernel(); dprintk("lockd: clnt proc returns %d\n", status); return status; diff --git a/fs/nfs/file.c b/fs/nfs/file.c index ec7e27d00bc..52ed5676258 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -592,7 +592,6 @@ static int do_getlk(struct file *filp, int cmd, struct file_lock *fl) struct inode *inode = filp->f_mapping->host; int status = 0; - lock_kernel(); /* Try local locking first */ posix_test_lock(filp, fl); if (fl->fl_type != F_UNLCK) { @@ -608,7 +607,6 @@ static int do_getlk(struct file *filp, int cmd, struct file_lock *fl) status = NFS_PROTO(inode)->lock(filp, cmd, fl); out: - unlock_kernel(); return status; out_noconflict: fl->fl_type = F_UNLCK; @@ -650,13 +648,11 @@ static int do_unlk(struct file *filp, int cmd, struct file_lock *fl) * If we're signalled while cleaning up locks on process exit, we * still need to complete the unlock. */ - lock_kernel(); /* Use local locking if mounted with "-onolock" */ if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)) status = NFS_PROTO(inode)->lock(filp, cmd, fl); else status = do_vfs_lock(filp, fl); - unlock_kernel(); return status; } @@ -673,13 +669,11 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl) if (status != 0) goto out; - lock_kernel(); /* Use local locking if mounted with "-onolock" */ if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)) status = NFS_PROTO(inode)->lock(filp, cmd, fl); else status = do_vfs_lock(filp, fl); - unlock_kernel(); if (status < 0) goto out; /* -- cgit v1.2.3 From 55ae1aabfb108106dd095de2578ceef1c755a8b8 Mon Sep 17 00:00:00 2001 From: Ricardo Labiaga Date: Wed, 1 Apr 2009 09:23:03 -0400 Subject: nfs41: Add backchannel processing support to RPC state machine 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. Introduce a new transmit state for the client to reply on to backchannel requests. This new state simply reserves the transport and issues the reply. In case of a connection related error, disconnects the transport and drops the reply. It requires the forechannel to re-establish the connection and the server to retransmit the request, as stated in NFSv4.1 section 2.9.2 "Client and Server Transport Behavior". Note: There is no need to loop attempting to reserve the transport. If EAGAIN is returned by xprt_prepare_transmit(), return with tk_status == 0, setting tk_action to call_bc_transmit. rpc_execute() will invoke it again after the task is taken off the sleep queue. [nfs41: rpc_run_bc_task() need not be exported outside RPC module] [nfs41: New call_bc_transmit RPC state] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy [nfs41: Backchannel: No need to loop in call_bc_transmit()] Signed-off-by: Andy Adamson Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy [rpc_count_iostats incorrectly exits early] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy [Convert rpc_reply_expected() to inline function] [Remove unnecessary BUG_ON()] [Rename variable] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- include/linux/sunrpc/sched.h | 2 + include/linux/sunrpc/xprt.h | 12 +++++ net/sunrpc/clnt.c | 117 ++++++++++++++++++++++++++++++++++++++++++- net/sunrpc/stats.c | 6 ++- net/sunrpc/sunrpc.h | 37 ++++++++++++++ net/sunrpc/xprt.c | 38 +++++++++++--- 6 files changed, 203 insertions(+), 9 deletions(-) create mode 100644 net/sunrpc/sunrpc.h diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index 177376880fa..401097781fc 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -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 *); diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index beae030e80b..55c6c37e249 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -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 */ diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index aca3ab6fc14..f3e93b8eb90 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -36,7 +36,9 @@ #include #include #include +#include +#include "sunrpc.h" #ifdef RPC_DEBUG # define RPCDBG_FACILITY RPCDBG_CALL @@ -63,6 +65,9 @@ static void call_decode(struct rpc_task *task); static void call_bind(struct rpc_task *task); static void call_bind_status(struct rpc_task *task); static void call_transmit(struct rpc_task *task); +#if defined(CONFIG_NFS_V4_1) +static void call_bc_transmit(struct rpc_task *task); +#endif /* CONFIG_NFS_V4_1 */ static void call_status(struct rpc_task *task); static void call_transmit_status(struct rpc_task *task); static void call_refresh(struct rpc_task *task); @@ -613,6 +618,50 @@ 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; + 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); + 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_bc_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; +} +#endif /* CONFIG_NFS_V4_1 */ + void rpc_call_start(struct rpc_task *task) { @@ -1098,7 +1147,7 @@ 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); @@ -1133,6 +1182,72 @@ call_transmit_status(struct rpc_task *task) } } +#if defined(CONFIG_NFS_V4_1) +/* + * 5b. Send the backchannel RPC reply. On error, drop the reply. In + * addition, disconnect on connectivity errors. + */ +static void +call_bc_transmit(struct rpc_task *task) +{ + struct rpc_rqst *req = task->tk_rqstp; + + BUG_ON(task->tk_status != 0); + task->tk_status = xprt_prepare_transmit(task); + if (task->tk_status == -EAGAIN) { + /* + * Could not reserve the transport. Try again after the + * transport is released. + */ + task->tk_status = 0; + task->tk_action = call_bc_transmit; + return; + } + + task->tk_action = rpc_exit_task; + if (task->tk_status < 0) { + printk(KERN_NOTICE "RPC: Could not send backchannel reply " + "error: %d\n", task->tk_status); + return; + } + + xprt_transmit(task); + xprt_end_transmit(task); + dprint_status(task); + switch (task->tk_status) { + case 0: + /* Success */ + break; + case -EHOSTDOWN: + case -EHOSTUNREACH: + case -ENETUNREACH: + case -ETIMEDOUT: + /* + * Problem reaching the server. Disconnect and let the + * forechannel reestablish the connection. The server will + * have to retransmit the backchannel request and we'll + * reprocess it. Since these ops are idempotent, there's no + * need to cache our reply at this time. + */ + printk(KERN_NOTICE "RPC: Could not send backchannel reply " + "error: %d\n", task->tk_status); + xprt_conditional_disconnect(task->tk_xprt, + req->rq_connect_cookie); + break; + default: + /* + * We were unable to reply and will have to drop the + * request. The server should reconnect and retransmit. + */ + BUG_ON(task->tk_status == -EAGAIN); + printk(KERN_NOTICE "RPC: Could not send backchannel reply " + "error: %d\n", task->tk_status); + break; + } + rpc_wake_up_queued_task(&req->rq_xprt->pending, task); +} +#endif /* CONFIG_NFS_V4_1 */ + /* * 6. Sort out the RPC call status */ diff --git a/net/sunrpc/stats.c b/net/sunrpc/stats.c index 1ef6e46d9da..8487aa0f1f5 100644 --- a/net/sunrpc/stats.c +++ b/net/sunrpc/stats.c @@ -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++; diff --git a/net/sunrpc/sunrpc.h b/net/sunrpc/sunrpc.h new file mode 100644 index 00000000000..5d9dd742264 --- /dev/null +++ b/net/sunrpc/sunrpc.h @@ -0,0 +1,37 @@ +/****************************************************************************** + +(c) 2008 NetApp. All Rights Reserved. + +NetApp 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 + +static inline int rpc_reply_expected(struct rpc_task *task) +{ + return (task->tk_msg.rpc_proc != NULL) && + (task->tk_msg.rpc_proc->p_decode != NULL); +} + +#endif /* _NET_SUNRPC_SUNRPC_H */ + diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 52739f82df1..0eea2bfe111 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -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 #include +#include "sunrpc.h" + /* * Local variables */ @@ -873,7 +876,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, @@ -908,8 +914,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); } @@ -982,11 +993,17 @@ 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 is_bc_request; if (!(req = task->tk_rqstp)) return; + + /* Preallocated backchannel request? */ + is_bc_request = bc_prealloc(req); + + xprt = req->rq_xprt; rpc_count_iostats(task); spin_lock_bh(&xprt->transport_lock); xprt->ops->release_xprt(xprt, task); @@ -999,10 +1016,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 (is_bc_request) + return; + memset(req, 0, sizeof(*req)); /* mark unused */ dprintk("RPC: %5u release request %p\n", task->tk_pid, req); -- cgit v1.2.3 From 0d90ba1cd416525c4825c111db862d8b15a02e9b Mon Sep 17 00:00:00 2001 From: Ricardo Labiaga Date: Wed, 1 Apr 2009 09:23:04 -0400 Subject: nfs41: Backchannel callback service helper routines Executes the backchannel task on the RPC state machine using the existing open connection previously established by the client. Signed-off-by: Ricardo Labiaga nfs41: Add bc_svc.o to sunrpc Makefile. [nfs41: bc_send() does not need to be exported outside RPC module] [nfs41: xprt_free_bc_request() need not be exported outside RPC module] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy [Update copyright] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- 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 deletion(-) create mode 100644 net/sunrpc/bc_svc.c diff --git a/include/linux/sunrpc/bc_xprt.h b/include/linux/sunrpc/bc_xprt.h index 5965ae4f902..6508f0dc0ef 100644 --- a/include/linux/sunrpc/bc_xprt.h +++ b/include/linux/sunrpc/bc_xprt.h @@ -29,12 +29,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include #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); #else /* CONFIG_NFS_V4_1 */ static inline int xprt_setup_backchannel(struct rpc_xprt *xprt, unsigned int min_reqs) diff --git a/net/sunrpc/Makefile b/net/sunrpc/Makefile index 4a01f9684b8..db73fd2a3f0 100644 --- a/net/sunrpc/Makefile +++ b/net/sunrpc/Makefile @@ -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 diff --git a/net/sunrpc/bc_svc.c b/net/sunrpc/bc_svc.c new file mode 100644 index 00000000000..13f214f5312 --- /dev/null +++ b/net/sunrpc/bc_svc.c @@ -0,0 +1,81 @@ +/****************************************************************************** + +(c) 2007 Network Appliance, Inc. All Rights Reserved. +(c) 2009 NetApp. All Rights Reserved. + +NetApp 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. + +******************************************************************************/ + +/* + * The NFSv4.1 callback service helper routines. + * They implement the transport level processing required to send the + * reply over an existing open connection previously established by the client. + */ + +#if defined(CONFIG_NFS_V4_1) + +#include + +#include +#include +#include + +#define RPCDBG_FACILITY RPCDBG_SVCDSP + +void bc_release_request(struct rpc_task *task) +{ + struct rpc_rqst *req = task->tk_rqstp; + + dprintk("RPC: bc_release_request: task= %p\n", task); + + /* + * Release this request only if it's a backchannel + * preallocated request + */ + if (!bc_prealloc(req)) + return; + xprt_free_bc_request(req); +} + +/* Empty callback ops */ +static const struct rpc_call_ops nfs41_callback_ops = { +}; + + +/* + * Send the callback reply + */ +int bc_send(struct rpc_rqst *req) +{ + struct rpc_task *task; + int ret; + + dprintk("RPC: bc_send req= %p\n", req); + task = rpc_run_bc_task(req, &nfs41_callback_ops); + if (IS_ERR(task)) + ret = PTR_ERR(task); + else { + BUG_ON(atomic_read(&task->tk_count) != 1); + ret = task->tk_status; + rpc_put_task(task); + } + return ret; + dprintk("RPC: bc_send ret= %d \n", ret); +} + +#endif /* CONFIG_NFS_V4_1 */ diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index e3e3a57116f..8a721867b60 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -2183,6 +2183,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_close, .destroy = xs_destroy, .print_stats = xs_tcp_print_stats, -- cgit v1.2.3 From 1cad7ea6fe98dc414bd3df55275c147bd15ebf97 Mon Sep 17 00:00:00 2001 From: Ricardo Labiaga Date: Wed, 1 Apr 2009 09:23:06 -0400 Subject: nfs41: Refactor svc_process() 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. Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- net/sunrpc/svc.c | 80 +++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 30 deletions(-) diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 8847add6ca1..bfda66db2f4 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -970,20 +970,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) { 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; @@ -993,19 +991,6 @@ 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; /* Will be turned off only when NFSv4 Sessions are used */ @@ -1014,17 +999,13 @@ svc_process(struct svc_rqst *rqstp) /* 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; @@ -1147,7 +1128,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 */ @@ -1161,12 +1142,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 */ @@ -1219,6 +1194,51 @@ err_bad: } EXPORT_SYMBOL_GPL(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. */ -- cgit v1.2.3 From 4d6bbb6233c9cf23822a2f66f8470c9f40854b77 Mon Sep 17 00:00:00 2001 From: Ricardo Labiaga Date: Wed, 1 Apr 2009 09:23:07 -0400 Subject: nfs41: Backchannel bc_svc_process() 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. [nfs41: Preallocate rpc_rqst receive buffer for handling callbacks] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy [Move bc_svc_process() declaration to correct patch] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- include/linux/sunrpc/svc.h | 2 ++ net/sunrpc/svc.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 4a8afbd6200..16043c4a8bf 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -419,6 +419,8 @@ int svc_set_num_threads(struct svc_serv *, struct svc_pool *, int); int svc_pool_stats_open(struct svc_serv *serv, struct file *file); 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 int, const unsigned short, const unsigned short); diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index bfda66db2f4..06b52e465f4 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -25,6 +25,7 @@ #include #include #include +#include #define RPCDBG_FACILITY RPCDBG_SVCDSP @@ -1239,6 +1240,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_rcv_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. */ -- cgit v1.2.3 From a43cde94feded0f65fce36330614691c650ae8fe Mon Sep 17 00:00:00 2001 From: Ricardo Labiaga Date: Wed, 1 Apr 2009 09:23:08 -0400 Subject: nfs41: Implement NFSv4.1 callback service process. 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. 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. Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy [nfs41: Add a reference to svc_serv during callback service bring up] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy [Type check arguments of nfs_callback_up] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy [nfs41: save svc_serv in nfs_callback_info] Signed-off-by: Benny Halevy [Removal of ugly #ifdefs] [nfs41: Update to removal of ugly #ifdefs] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- fs/nfs/callback.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- fs/nfs/callback.h | 7 ++++ 2 files changed, 115 insertions(+), 4 deletions(-) diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index 4b1313eda6f..47092889806 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -17,6 +17,9 @@ #include #include #include +#if defined(CONFIG_NFS_V4_1) +#include +#endif #include @@ -28,6 +31,7 @@ struct nfs_callback_data { unsigned int users; + struct svc_serv *serv; struct svc_rqst *rqst; struct task_struct *task; }; @@ -131,6 +135,99 @@ out_err: return ERR_PTR(ret); } +#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]); +} + +static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, + struct svc_serv *serv, struct rpc_xprt *xprt, + struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp)) +{ + if (minorversion) { + *rqstpp = nfs41_callback_up(serv, xprt); + *callback_svc = nfs41_callback_svc; + } + return minorversion; +} + +static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, + struct nfs_callback_data *cb_info) +{ + if (minorversion) + xprt->bc_serv = cb_info->serv; +} +#else +static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, + struct svc_serv *serv, struct rpc_xprt *xprt, + struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp)) +{ + return 0; +} + +static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, + struct nfs_callback_data *cb_info) +{ +} +#endif /* CONFIG_NFS_V4_1 */ + /* * Bring up the callback thread if it is not already up. */ @@ -141,21 +238,25 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt) int (*callback_svc)(void *vrqstp); char svc_name[12]; int ret = 0; + int minorversion_setup; 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) { + nfs_callback_bc_serv(minorversion, xprt, &nfs_callback_info); goto out; + } serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL); if (!serv) { ret = -ENOMEM; goto out_err; } - if (!minorversion) { + minorversion_setup = nfs_minorversion_callback_svc_setup(minorversion, + serv, xprt, &rqstp, &callback_svc); + if (!minorversion_setup) { + /* v4.0 callback setup */ rqstp = nfs4_callback_up(serv); callback_svc = nfs4_callback_svc; - } else { - BUG(); /* for now */ } if (IS_ERR(rqstp)) { @@ -166,6 +267,7 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt) svc_sock_update_bufs(serv); sprintf(svc_name, "nfsv4.%u-svc", minorversion); + nfs_callback_info.serv = serv; nfs_callback_info.rqst = rqstp; nfs_callback_info.task = kthread_run(callback_svc, nfs_callback_info.rqst, @@ -173,6 +275,7 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt) 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; @@ -205,6 +308,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; } diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h index 9b907f408b8..29123b5604f 100644 --- a/fs/nfs/callback.h +++ b/fs/nfs/callback.h @@ -70,6 +70,13 @@ extern void nfs_callback_down(void); #define nfs_callback_down() do {} while(0) #endif +/* + * nfs41: Callbacks are expected to not cause substantial latency, + * so we limit their concurrency to 1 by setting up the maximum number + * of slots for the backchannel. + */ +#define NFS41_BC_MIN_CALLBACKS 1 + extern unsigned int nfs_callback_set_tcpport; extern unsigned short nfs_callback_tcpport; extern unsigned short nfs_callback_tcpport6; -- cgit v1.2.3 From 7652e5a09ba319241607b22d9055ce93fd5b8039 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:23:09 -0400 Subject: nfs41: sunrpc: provide functions to create and destroy a svc_xprt for backchannel use 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. Signed-off-by: Benny Halevy --- include/linux/sunrpc/svcsock.h | 2 ++ net/sunrpc/svcsock.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/include/linux/sunrpc/svcsock.h b/include/linux/sunrpc/svcsock.h index 483e10380aa..6bb1ec4ae31 100644 --- a/include/linux/sunrpc/svcsock.h +++ b/include/linux/sunrpc/svcsock.h @@ -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 diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 9d504234af4..a2a03e50053 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -1327,3 +1327,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); -- cgit v1.2.3 From 9c9f3f5fa62cc4959e4d4d1cf1ec74f2d6ac1197 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:23:10 -0400 Subject: nfs41: sunrpc: add a struct svc_xprt pointer to struct svc_serv for backchannel use This svc_xprt is passed on to the callback service thread to be later used to processes incoming svc_rqst's Signed-off-by: Benny Halevy --- include/linux/sunrpc/svc.h | 1 + net/sunrpc/svc.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 16043c4a8bf..ea8009695c6 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -103,6 +103,7 @@ struct svc_serv { 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 */ + struct svc_xprt *bc_xprt; #endif /* CONFIG_NFS_V4_1 */ }; diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 06b52e465f4..b35048fabe2 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -487,6 +487,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); -- cgit v1.2.3 From 8f975242352e92898dc641ebff0d24808f39848a Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:23:11 -0400 Subject: nfs41: create a svc_xprt for nfs41 callback thread and use for incoming callbacks Signed-off-by: Benny Halevy --- fs/nfs/callback.c | 17 ++++++++++++++++- net/sunrpc/svc.c | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index 47092889806..37815f3216a 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -185,16 +185,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; } static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index b35048fabe2..6b90ce439c0 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -1258,6 +1258,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; -- cgit v1.2.3 From e82dc22dac6525a2f365a1d53c0483252d4aa38e Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:23:14 -0400 Subject: nfs41: Allow NFSv4 and NFSv4.1 callback services to coexist 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. Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- fs/nfs/callback.c | 50 ++++++++++++++++++++++++-------------------------- fs/nfs/callback.h | 7 ++----- fs/nfs/client.c | 2 +- 3 files changed, 27 insertions(+), 32 deletions(-) diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index 37815f3216a..e69b8f61189 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -36,7 +36,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; @@ -60,7 +60,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) @@ -174,8 +174,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; } @@ -251,13 +249,14 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt) struct svc_serv *serv = NULL; struct svc_rqst *rqstp; int (*callback_svc)(void *vrqstp); + struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; char svc_name[12]; int ret = 0; int minorversion_setup; mutex_lock(&nfs_callback_mutex); - if (nfs_callback_info.users++ || nfs_callback_info.task != NULL) { - nfs_callback_bc_serv(minorversion, xprt, &nfs_callback_info); + if (cb_info->users++ || cb_info->task != NULL) { + nfs_callback_bc_serv(minorversion, xprt, cb_info); goto out; } serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL); @@ -282,17 +281,14 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt) svc_sock_update_bufs(serv); sprintf(svc_name, "nfsv4.%u-svc", minorversion); - nfs_callback_info.serv = serv; - nfs_callback_info.rqst = rqstp; - 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 = rqstp; + 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: @@ -309,23 +305,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); } diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h index 29123b5604f..88d2e1135b5 100644 --- a/fs/nfs/callback.h +++ b/fs/nfs/callback.h @@ -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, struct rpc_xprt *xprt); -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 */ /* * nfs41: Callbacks are expected to not cause substantial latency, diff --git a/fs/nfs/client.c b/fs/nfs/client.c index df2b40d1883..ac6575c4da3 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -181,7 +181,7 @@ static void nfs4_destroy_callback(struct nfs_client *clp) { #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 */ } -- cgit v1.2.3 From 0b5b7ae0a853c91015bb3b1729166ca65f693322 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:23:15 -0400 Subject: nfs41: Setup the backchannel 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. Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy [nfs41: change nfs4_put_session(nfs4_session**) to nfs4_destroy_session(nfs_session*)] Signed-off-by: Alexandros Batsakis Signed-off-by: Benny Halevy [moved xprt_setup_backchannel from nfs4_init_session to nfs4_init_backchannel] Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy --- fs/nfs/client.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index ac6575c4da3..4f75ec593be 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -37,6 +37,7 @@ #include #include #include +#include #include @@ -1096,6 +1097,14 @@ static int nfs4_init_callback(struct nfs_client *clp) int error; if (clp->rpc_ops->version == 4) { + if (nfs4_has_session(clp)) { + error = xprt_setup_backchannel( + clp->cl_rpcclient->cl_xprt, + NFS41_BC_MIN_CALLBACKS); + if (error < 0) + return error; + } + error = nfs_callback_up(clp->cl_minorversion, clp->cl_rpcclient->cl_xprt); if (error < 0) { -- cgit v1.2.3 From 0f91421e8ea986c990c01bf091ceff1d59c165ff Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:23:16 -0400 Subject: nfs41: Client indicates presence of NFSv4.1 callback channel. Set the SESSION4_BACK_CHAN flag to indicate the client supports a backchannel. Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- fs/nfs/nfs4proc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index f1f087b483c..4213c881499 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4598,7 +4598,7 @@ static int _nfs4_proc_create_session(struct nfs_client *clp) int status; nfs4_init_channel_attrs(&args); - args.flags = (SESSION4_PERSIST); + args.flags = (SESSION4_PERSIST | SESSION4_BACK_CHAN); status = rpc_call_sync(session->clp->cl_rpcclient, &msg, 0); -- cgit v1.2.3 From 343952fa5aac888934ffc203abed26a823400eb6 Mon Sep 17 00:00:00 2001 From: Rahul Iyer Date: Wed, 1 Apr 2009 09:23:17 -0400 Subject: nfs41: Get the rpc_xprt * from the rpc_rqst instead of the rpc_clnt. 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. Signed-off-by: Rahul Iyer Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- net/sunrpc/xprt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 0eea2bfe111..c144611223f 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -195,8 +195,8 @@ EXPORT_SYMBOL_GPL(xprt_load_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) @@ -858,7 +858,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); } /** -- cgit v1.2.3 From 5a0ffe544c54f62be99751e369f4d0f44bd5ee19 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 1 Apr 2009 09:23:18 -0400 Subject: nfs41: Release backchannel resources associated with session 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. Signed-off-by: Ricardo Labiaga Signed-off-by: Andy Adamson Signed-off-by: Benny Halevy --- fs/nfs/nfs4proc.c | 6 ++++++ fs/nfs/super.c | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 4213c881499..5da939d577d 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -49,6 +49,7 @@ #include #include #include +#include #include "nfs4_fs.h" #include "delegation.h" @@ -4481,6 +4482,11 @@ struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp) void nfs4_destroy_session(struct nfs4_session *session) { + nfs4_proc_destroy_session(session); + dprintk("%s Destroy backchannel for xprt %p\n", + __func__, session->clp->cl_rpcclient->cl_xprt); + xprt_destroy_backchannel(session->clp->cl_rpcclient->cl_xprt, + NFS41_BC_MIN_CALLBACKS); nfs4_destroy_slot_table(session); kfree(session); } diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 73db6f8c145..69804a8245f 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -2488,7 +2488,6 @@ static void nfs4_kill_super(struct super_block *sb) dprintk("--> %s\n", __func__); nfs_super_return_all_delegations(sb); kill_anon_super(sb); - nfs4_renewd_prepare_shutdown(server); nfs_fscache_release_super_cookie(sb); nfs_free_server(server); -- cgit v1.2.3 From b8f2ef84b079ceb22b42d6d353609db7eb8efa93 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:23:19 -0400 Subject: nfs41: store minorversion in cb_compound_hdr_arg Signed-off-by: Benny Halevy --- fs/nfs/callback.h | 1 + fs/nfs/callback_xdr.c | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h index 88d2e1135b5..51b15c2d3a5 100644 --- a/fs/nfs/callback.h +++ b/fs/nfs/callback.h @@ -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; }; diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index dd0ef34b584..91f6f74ffea 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -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; } -- cgit v1.2.3 From 48a9e2d2289ed4b5053b3986d99709e9b07a0923 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:23:20 -0400 Subject: nfs41: decode minorversion 1 cb_compound header 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. Signed-off-by: Benny Halevy [nfs41: no need to break read_buf in decode_compound_hdr_arg] See http://linux-nfs.org/pipermail/pnfs/2009-June/007844.html Signed-off-by: Benny Halevy --- fs/nfs/callback.h | 1 - fs/nfs/callback_xdr.c | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h index 51b15c2d3a5..a8ffa9d6ea2 100644 --- a/fs/nfs/callback.h +++ b/fs/nfs/callback.h @@ -27,7 +27,6 @@ struct cb_compound_hdr_arg { unsigned int taglen; const char *tag; unsigned int minorversion; - unsigned int callback_ident; unsigned nops; }; diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index 91f6f74ffea..f6cc79b1d1f 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -147,14 +147,15 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound 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++; /* 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); -- cgit v1.2.3 From 45377b94edea18f53dd3ba4d46d94de4bb7c00b5 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:23:21 -0400 Subject: nfs41: callback numbers definitions Signed-off-by: Benny Halevy --- fs/nfs/callback.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h index a8ffa9d6ea2..7ba42b0d16c 100644 --- a/fs/nfs/callback.h +++ b/fs/nfs/callback.h @@ -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, }; -- cgit v1.2.3 From 34bc47c941a074f91c2455b4b08503d02c74b878 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:23:22 -0400 Subject: nfs41: consider minorversion in callback_xdr:process_op Note that this patch changes the nfsv4.0 behavior also when CONFIG_NFS_V4_1 is not defined where NFS4ERR_MINOR_VERS_MISMATCH will be returned if the client received a CB_COMPOUND with minorversion != 0. Previously, it would have returned NFS4ERR_OP_ILLEGAL for CB_SEQUENCE. (or if the server is broken and sent OP_CB_GETATTR or OP_CB_RECALL with minorversion!=0, they would have been processed normally. Signed-off-by: Benny Halevy [nfs41: refactor op preprocessing out of process_op] See http://linux-nfs.org/pipermail/pnfs/2009-June/007845.html [nfs41: define CB_NOTIFY_DEVICEID as not supported] Signed-off-by: Benny Halevy --- fs/nfs/callback.h | 1 + fs/nfs/callback_xdr.c | 85 ++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 72 insertions(+), 14 deletions(-) diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h index 7ba42b0d16c..80fd8a82964 100644 --- a/fs/nfs/callback.h +++ b/fs/nfs/callback.h @@ -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, }; diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index f6cc79b1d1f..41c5be1c1be 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -356,31 +356,87 @@ out: return status; } -static __be32 process_op(struct svc_rqst *rqstp, +#if defined(CONFIG_NFS_V4_1) + +static __be32 +preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) +{ + switch (op_nr) { + case OP_CB_GETATTR: + case OP_CB_RECALL: + *op = &callback_ops[op_nr]; + break; + + case OP_CB_LAYOUTRECALL: + case OP_CB_NOTIFY_DEVICEID: + 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: + return htonl(NFS4ERR_NOTSUPP); + + default: + return htonl(NFS4ERR_OP_ILLEGAL); + } + + return htonl(NFS_OK); +} + +#else /* CONFIG_NFS_V4_1 */ + +static __be32 +preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) +{ + return htonl(NFS4ERR_MINOR_VERS_MISMATCH); +} + +#endif /* CONFIG_NFS_V4_1 */ + +static __be32 +preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op) +{ + switch (op_nr) { + case OP_CB_GETATTR: + case OP_CB_RECALL: + *op = &callback_ops[op_nr]; + break; + default: + return htonl(NFS4ERR_OP_ILLEGAL); + } + + return htonl(NFS_OK); +} + +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)) { - 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); - } + if (unlikely(status)) { + status = htonl(NFS4ERR_OP_ILLEGAL); + goto out; } + dprintk("%s: minorversion=%d nop=%d op_nr=%u\n", + __func__, minorversion, nop, op_nr); + + status = minorversion ? preprocess_nfs41_op(nop, op_nr, &op) : + preprocess_nfs4_op(op_nr, &op); + if (status == htonl(NFS4ERR_OP_ILLEGAL)) + op_nr = OP_CB_ILLEGAL; +out: maxlen = xdr_out->end - xdr_out->p; if (maxlen > 0 && maxlen < PAGE_SIZE) { if (likely(status == 0 && op->decode_args != NULL)) @@ -428,7 +484,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++; } -- cgit v1.2.3 From 2d9b9ec344b19b7b65c732b7000114df57684140 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:23:24 -0400 Subject: nfs41: cb_sequence protocol level data structures Signed-off-by: Benny Halevy --- fs/nfs/callback.h | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h index 80fd8a82964..2b933ce064d 100644 --- a/fs/nfs/callback.h +++ b/fs/nfs/callback.h @@ -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 { + struct nfs4_sessionid rcl_sessionid; + uint32_t rcl_nrefcalls; + struct referring_call *rcl_refcalls; +}; + +struct cb_sequenceargs { + struct sockaddr_in *csa_addr; + struct nfs4_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; + struct nfs4_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); -- cgit v1.2.3 From d49433e1e3bb144a5752ce2a8ba1139dc519df1a Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:23:25 -0400 Subject: nfs41: cb_sequence proc implementation Currently, just free up any referring calls information. Signed-off-by: Benny Halevy [nfs41: fix csr_{,target}highestslotid] Signed-off-by: Benny Halevy --- fs/nfs/callback.h | 4 ++++ fs/nfs/callback_proc.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h index 2b933ce064d..576f51f4dc5 100644 --- a/fs/nfs/callback.h +++ b/fs/nfs/callback.h @@ -103,6 +103,9 @@ struct cb_sequenceres { uint32_t csr_target_highestslotid; }; +extern unsigned nfs4_callback_sequence(struct cb_sequenceargs *args, + struct cb_sequenceres *res); + #endif /* CONFIG_NFS_V4_1 */ extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res); @@ -119,6 +122,7 @@ extern void nfs_callback_down(int minorversion); * of slots for the backchannel. */ #define NFS41_BC_MIN_CALLBACKS 1 +#define NFS41_BC_MAX_CALLBACKS 1 extern unsigned int nfs_callback_set_tcpport; extern unsigned short nfs_callback_tcpport; diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index f7e83e23cf9..f731bbe7ac6 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -101,3 +101,31 @@ 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 - 1; + res->csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; + + dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); + res->csr_status = status; + return status; +} + +#endif /* CONFIG_NFS_V4_1 */ -- cgit v1.2.3 From 4aece6a19cf7f474f15eb861ba74db4479884ce3 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:23:26 -0400 Subject: nfs41: cb_sequence xdr implementation [nfs41: get rid of READMEM and COPYMEM for callback_xdr.c] Signed-off-by: Benny Halevy [nfs41: get rid of READ64 in callback_xdr.c] See http://linux-nfs.org/pipermail/pnfs/2009-June/007846.html Signed-off-by: Benny Halevy --- fs/nfs/callback_xdr.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 170 insertions(+), 2 deletions(-) diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index 41c5be1c1be..56a3cc51010 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -20,6 +20,11 @@ 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 typedef __be32 (*callback_process_op_t)(void *, void *); @@ -207,6 +212,122 @@ out: return status; } +#if defined(CONFIG_NFS_V4_1) + +static unsigned decode_sessionid(struct xdr_stream *xdr, + struct nfs4_sessionid *sid) +{ + uint32_t *p; + int len = NFS4_MAX_SESSIONID_LEN; + + p = read_buf(xdr, len); + if (unlikely(p == NULL)) + return htonl(NFS4ERR_RESOURCE);; + + memcpy(sid->data, 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; @@ -358,12 +479,52 @@ out: #if defined(CONFIG_NFS_V4_1) +static unsigned encode_sessionid(struct xdr_stream *xdr, + const struct nfs4_sessionid *sid) +{ + uint32_t *p; + int len = NFS4_MAX_SESSIONID_LEN; + + 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; +} + static __be32 preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) { switch (op_nr) { case OP_CB_GETATTR: case OP_CB_RECALL: + case OP_CB_SEQUENCE: *op = &callback_ops[op_nr]; break; @@ -374,7 +535,6 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) 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: return htonl(NFS4ERR_NOTSUPP); @@ -512,7 +672,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 */ }; /* -- cgit v1.2.3 From 281fe15dc1d6ad46992f18b7a6644269ec5f7138 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Wed, 1 Apr 2009 09:23:27 -0400 Subject: nfs41: verify CB_SEQUENCE position in callback compound 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. Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy [nfs41: refactor op preprocessing out of process_op] Signed-off-by: Benny Halevy --- fs/nfs/callback_xdr.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index 56a3cc51010..537f21da6e5 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -521,6 +521,14 @@ out: static __be32 preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) { + if (op_nr == OP_CB_SEQUENCE) { + if (nop != 0) + return htonl(NFS4ERR_SEQUENCE_POS); + } else { + if (nop == 0) + return htonl(NFS4ERR_OP_NOT_IN_SESSION); + } + switch (op_nr) { case OP_CB_GETATTR: case OP_CB_RECALL: -- cgit v1.2.3 From dd2b63d049480979016b959abc2d141cdddb1389 Mon Sep 17 00:00:00 2001 From: Ricardo Labiaga Date: Wed, 1 Apr 2009 09:23:28 -0400 Subject: nfs41: Rename rq_received to rq_reply_bytes_recvd The 'rq_received' member of 'struct rpc_rqst' is used to track when we have received a reply to our request. With v4.1, the backchannel can now accept callback requests over the existing connection. Rename this field to make it clear that it is only used for tracking reply bytes and not all bytes received on the connection. Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- include/linux/sunrpc/xprt.h | 3 ++- net/sunrpc/backchannel_rqst.c | 2 +- net/sunrpc/clnt.c | 8 ++++---- net/sunrpc/stats.c | 2 +- net/sunrpc/xprt.c | 15 ++++++++------- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 55c6c37e249..1175d58efc2 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -67,7 +67,8 @@ struct rpc_rqst { struct rpc_task * rq_task; /* RPC task data */ __be32 rq_xid; /* request XID */ int rq_cong; /* has incremented xprt->cong */ - int rq_received; /* receive completed */ + int rq_reply_bytes_recvd; /* number of reply */ + /* bytes received */ u32 rq_seqno; /* gss seq no. used on req. */ int rq_enc_pages_num; struct page **rq_enc_pages; /* scratch pages for use by diff --git a/net/sunrpc/backchannel_rqst.c b/net/sunrpc/backchannel_rqst.c index f56e18a2349..5a7d342e308 100644 --- a/net/sunrpc/backchannel_rqst.c +++ b/net/sunrpc/backchannel_rqst.c @@ -230,7 +230,7 @@ struct rpc_rqst *xprt_alloc_bc_request(struct rpc_xprt *xprt) if (req != NULL) { set_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state); - req->rq_received = 0; + req->rq_reply_bytes_recvd = 0; req->rq_bytes_sent = 0; memcpy(&req->rq_private_buf, &req->rq_rcv_buf, sizeof(req->rq_private_buf)); diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index f3e93b8eb90..5bc2f45bddf 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -1258,8 +1258,8 @@ call_status(struct rpc_task *task) struct rpc_rqst *req = task->tk_rqstp; int status; - if (req->rq_received > 0 && !req->rq_bytes_sent) - task->tk_status = req->rq_received; + if (req->rq_reply_bytes_recvd > 0 && !req->rq_bytes_sent) + task->tk_status = req->rq_reply_bytes_recvd; dprint_status(task); @@ -1376,7 +1376,7 @@ call_decode(struct rpc_task *task) /* * Ensure that we see all writes made by xprt_complete_rqst() - * before it changed req->rq_received. + * before it changed req->rq_reply_bytes_recvd. */ smp_rmb(); req->rq_rcv_buf.len = req->rq_private_buf.len; @@ -1417,7 +1417,7 @@ out_retry: task->tk_status = 0; /* Note: rpc_verify_header() may have freed the RPC slot */ if (task->tk_rqstp == req) { - req->rq_received = req->rq_rcv_buf.len = 0; + req->rq_reply_bytes_recvd = req->rq_rcv_buf.len = 0; if (task->tk_client->cl_discrtry) xprt_conditional_disconnect(task->tk_xprt, req->rq_connect_cookie); diff --git a/net/sunrpc/stats.c b/net/sunrpc/stats.c index 8487aa0f1f5..1b4e6791ecf 100644 --- a/net/sunrpc/stats.c +++ b/net/sunrpc/stats.c @@ -156,7 +156,7 @@ void rpc_count_iostats(struct rpc_task *task) op_metrics->om_timeouts += task->tk_timeouts; op_metrics->om_bytes_sent += task->tk_bytes_sent; - op_metrics->om_bytes_recv += req->rq_received; + op_metrics->om_bytes_recv += req->rq_reply_bytes_recvd; queue = (long)req->rq_xtime - task->tk_start; if (queue < 0) diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index c144611223f..f412a852bc7 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -806,9 +806,10 @@ void xprt_complete_rqst(struct rpc_task *task, int copied) list_del_init(&req->rq_list); req->rq_private_buf.len = copied; - /* Ensure all writes are done before we update req->rq_received */ + /* Ensure all writes are done before we update */ + /* req->rq_reply_bytes_recvd */ smp_wmb(); - req->rq_received = copied; + req->rq_reply_bytes_recvd = copied; rpc_wake_up_queued_task(&xprt->pending, task); } EXPORT_SYMBOL_GPL(xprt_complete_rqst); @@ -823,7 +824,7 @@ static void xprt_timer(struct rpc_task *task) dprintk("RPC: %5u xprt_timer\n", task->tk_pid); spin_lock_bh(&xprt->transport_lock); - if (!req->rq_received) { + if (!req->rq_reply_bytes_recvd) { if (xprt->ops->timer) xprt->ops->timer(task); } else @@ -845,8 +846,8 @@ int xprt_prepare_transmit(struct rpc_task *task) dprintk("RPC: %5u xprt_prepare_transmit\n", task->tk_pid); spin_lock_bh(&xprt->transport_lock); - if (req->rq_received && !req->rq_bytes_sent) { - err = req->rq_received; + if (req->rq_reply_bytes_recvd && !req->rq_bytes_sent) { + err = req->rq_reply_bytes_recvd; goto out_unlock; } if (!xprt->ops->reserve_xprt(task)) @@ -875,7 +876,7 @@ 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 (!req->rq_reply_bytes_recvd) { if (list_empty(&req->rq_list) && rpc_reply_expected(task)) { /* * Add to the list only if we're expecting a reply @@ -914,7 +915,7 @@ 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 && rpc_reply_expected(task)) { + else if (!req->rq_reply_bytes_recvd && rpc_reply_expected(task)) { /* * Sleep on the pending queue since * we're expecting a reply. -- cgit v1.2.3 From 65fc64e547c794764a441e16e95bb76c0e256bd7 Mon Sep 17 00:00:00 2001 From: Ricardo Labiaga Date: Wed, 1 Apr 2009 09:23:30 -0400 Subject: nfs41: Backchannel: update cb_sequence args and results Change the type of cs_addr and csr_status to 'struct sockaddr' and '__be32' since the cb_sequence processing function will use existing functionality that expects these types. Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- fs/nfs/callback.h | 4 ++-- fs/nfs/callback_xdr.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h index 576f51f4dc5..07baa8254ca 100644 --- a/fs/nfs/callback.h +++ b/fs/nfs/callback.h @@ -84,7 +84,7 @@ struct referring_call_list { }; struct cb_sequenceargs { - struct sockaddr_in *csa_addr; + struct sockaddr *csa_addr; struct nfs4_sessionid csa_sessionid; uint32_t csa_sequenceid; uint32_t csa_slotid; @@ -95,7 +95,7 @@ struct cb_sequenceargs { }; struct cb_sequenceres { - uint32_t csr_status; + __be32 csr_status; struct nfs4_sessionid csr_sessionid; uint32_t csr_sequenceid; uint32_t csr_slotid; diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index 537f21da6e5..e5a2dac5f71 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -283,7 +283,7 @@ static unsigned decode_cb_sequence_args(struct svc_rqst *rqstp, if (unlikely(p == NULL)) goto out; - args->csa_addr = svc_addr_in(rqstp); + args->csa_addr = svc_addr(rqstp); args->csa_sequenceid = ntohl(*p++); args->csa_slotid = ntohl(*p++); args->csa_highestslotid = ntohl(*p++); -- cgit v1.2.3 From b73dafa7ac94ca8387f65c57cb63a7ffac91bf2c Mon Sep 17 00:00:00 2001 From: Ricardo Labiaga Date: Wed, 1 Apr 2009 09:23:31 -0400 Subject: nfs41: Backchannel: Refactor nfs4_reset_slot_table() Generalize nfs4_reset_slot_table() so it can be used to reset the backchannel slot table in addition to the forechannel slot table. Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- fs/nfs/nfs4proc.c | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 5da939d577d..a7b1d6c228c 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4370,19 +4370,21 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo) return status; } -/* Reset a slot table */ -static int nfs4_reset_slot_table(struct nfs4_session *session) +/* + * Reset a slot table + */ +static int nfs4_reset_slot_table(struct nfs4_slot_table *tbl, int max_slots, + int old_max_slots, int ivalue) { - struct nfs4_slot_table *tbl = &session->fc_slot_table; - int i, max_slots = session->fc_attrs.max_reqs; - int old_max_slots = session->fc_slot_table.max_slots; + int i; int ret = 0; - dprintk("--> %s: max_reqs=%u, tbl %p\n", __func__, - session->fc_attrs.max_reqs, tbl); + dprintk("--> %s: max_reqs=%u, tbl %p\n", __func__, max_slots, tbl); - /* Until we have dynamic slot table adjustment, insist - * upon the same slot table size */ + /* + * Until we have dynamic slot table adjustment, insist + * upon the same slot table size + */ if (max_slots != old_max_slots) { dprintk("%s reset slot table does't match old\n", __func__); @@ -4391,7 +4393,7 @@ static int nfs4_reset_slot_table(struct nfs4_session *session) } spin_lock(&tbl->slot_tbl_lock); for (i = 0; i < max_slots; ++i) - tbl->slots[i].seq_nr = 1; + tbl->slots[i].seq_nr = ivalue; tbl->highest_used_slotid = -1; spin_unlock(&tbl->slot_tbl_lock); dprintk("%s: tbl=%p slots=%p max_slots=%d\n", __func__, @@ -4401,6 +4403,20 @@ out: return ret; } +/* + * Reset the forechannel and backchannel slot tables + */ +static int nfs4_reset_slot_tables(struct nfs4_session *session) +{ + int status; + + status = nfs4_reset_slot_table(&session->fc_slot_table, + session->fc_attrs.max_reqs, + session->fc_slot_table.max_slots, + 1); + return status; +} + /* * Initialize slot table */ @@ -4639,7 +4655,7 @@ int nfs4_proc_create_session(struct nfs_client *clp, int reset) /* Init or reset the fore channel */ if (reset) - status = nfs4_reset_slot_table(session); + status = nfs4_reset_slot_tables(session); else status = nfs4_init_slot_table(session); dprintk("fore channel slot table initialization returned %d\n", status); -- cgit v1.2.3 From 050047ce71bcf60867d2af7a9dc965a9c6f15cb8 Mon Sep 17 00:00:00 2001 From: Ricardo Labiaga Date: Wed, 1 Apr 2009 09:23:32 -0400 Subject: nfs41: Backchannel: Refactor nfs4_init_slot_table() Generalize nfs4_init_slot_table() so it can be used to initialize the backchannel slot table in addition to the forechannel slot table. Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- fs/nfs/nfs4proc.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a7b1d6c228c..c3019ad8589 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4420,23 +4420,22 @@ static int nfs4_reset_slot_tables(struct nfs4_session *session) /* * Initialize slot table */ -static int nfs4_init_slot_table(struct nfs4_session *session) +static int nfs4_init_slot_table(struct nfs4_slot_table *tbl, + int max_slots, int ivalue) { - struct nfs4_slot_table *tbl = &session->fc_slot_table; - int i, max_slots = session->fc_attrs.max_reqs; + int i; struct nfs4_slot *slot; int ret = -ENOMEM; BUG_ON(max_slots > NFS4_MAX_SLOT_TABLE); - dprintk("--> %s: max_reqs=%u\n", __func__, - session->fc_attrs.max_reqs); + dprintk("--> %s: max_reqs=%u\n", __func__, max_slots); slot = kcalloc(max_slots, sizeof(struct nfs4_slot), GFP_KERNEL); if (!slot) goto out; for (i = 0; i < max_slots; ++i) - slot[i].seq_nr = 1; + slot[i].seq_nr = ivalue; ret = 0; spin_lock(&tbl->slot_tbl_lock); @@ -4456,11 +4455,24 @@ static int nfs4_init_slot_table(struct nfs4_session *session) out: dprintk("<-- %s: return %d\n", __func__, ret); return ret; + out_free: kfree(slot); goto out; } +/* + * Initialize the forechannel and backchannel tables + */ +static int nfs4_init_slot_tables(struct nfs4_session *session) +{ + int status; + + status = nfs4_init_slot_table(&session->fc_slot_table, + session->fc_attrs.max_reqs, 1); + return status; +} + /* Destroy the slot table */ static void nfs4_destroy_slot_table(struct nfs4_session *session) { @@ -4657,7 +4669,7 @@ int nfs4_proc_create_session(struct nfs_client *clp, int reset) if (reset) status = nfs4_reset_slot_tables(session); else - status = nfs4_init_slot_table(session); + status = nfs4_init_slot_tables(session); dprintk("fore channel slot table initialization returned %d\n", status); if (status) goto out; -- cgit v1.2.3 From f8625a6a4bb76207302be58453603d8e324df490 Mon Sep 17 00:00:00 2001 From: Ricardo Labiaga Date: Wed, 1 Apr 2009 09:23:33 -0400 Subject: nfs41: Backchannel: Add a backchannel slot table to the session Defines a new 'struct nfs4_slot_table' in the 'struct nfs4_session' for use by the backchannel. Initializes, resets, and destroys the backchannel slot table in the same manner the forechannel slot table is initialized, reset, and destroyed. The sequenceid for each slot in the backchannel slot table is initialized to 0, whereas the forechannel slotid's sequenceid is set to 1. Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- fs/nfs/nfs4proc.c | 48 +++++++++++++++++++++++++++++++++++------------ include/linux/nfs_fs_sb.h | 2 +- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index c3019ad8589..57dabb8a048 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4414,9 +4414,30 @@ static int nfs4_reset_slot_tables(struct nfs4_session *session) session->fc_attrs.max_reqs, session->fc_slot_table.max_slots, 1); + if (status) + return status; + + status = nfs4_reset_slot_table(&session->bc_slot_table, + session->bc_attrs.max_reqs, + session->bc_slot_table.max_slots, + 0); return status; } +/* Destroy the slot table */ +static void nfs4_destroy_slot_tables(struct nfs4_session *session) +{ + if (session->fc_slot_table.slots != NULL) { + kfree(session->fc_slot_table.slots); + session->fc_slot_table.slots = NULL; + } + if (session->bc_slot_table.slots != NULL) { + kfree(session->bc_slot_table.slots); + session->bc_slot_table.slots = NULL; + } + return; +} + /* * Initialize slot table */ @@ -4470,17 +4491,15 @@ static int nfs4_init_slot_tables(struct nfs4_session *session) status = nfs4_init_slot_table(&session->fc_slot_table, session->fc_attrs.max_reqs, 1); - return status; -} + if (status) + return status; -/* Destroy the slot table */ -static void nfs4_destroy_slot_table(struct nfs4_session *session) -{ - if (session->fc_slot_table.slots == NULL) - return; - kfree(session->fc_slot_table.slots); - session->fc_slot_table.slots = NULL; - return; + status = nfs4_init_slot_table(&session->bc_slot_table, + session->bc_attrs.max_reqs, 0); + if (status) + nfs4_destroy_slot_tables(session); + + return status; } struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp) @@ -4503,7 +4522,12 @@ struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp) tbl = &session->fc_slot_table; spin_lock_init(&tbl->slot_tbl_lock); - rpc_init_wait_queue(&tbl->slot_tbl_waitq, "Slot table"); + rpc_init_wait_queue(&tbl->slot_tbl_waitq, "ForeChannel Slot table"); + + tbl = &session->bc_slot_table; + spin_lock_init(&tbl->slot_tbl_lock); + rpc_init_wait_queue(&tbl->slot_tbl_waitq, "BackChannel Slot table"); + session->clp = clp; return session; } @@ -4515,7 +4539,7 @@ void nfs4_destroy_session(struct nfs4_session *session) __func__, session->clp->cl_rpcclient->cl_xprt); xprt_destroy_backchannel(session->clp->cl_rpcclient->cl_xprt, NFS41_BC_MIN_CALLBACKS); - nfs4_destroy_slot_table(session); + nfs4_destroy_slot_tables(session); kfree(session); } diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index d0902ccec9c..19fe15d1204 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -205,7 +205,7 @@ struct nfs4_session { struct nfs4_channel_attrs fc_attrs; struct nfs4_slot_table fc_slot_table; struct nfs4_channel_attrs bc_attrs; - /* back channel has one slot */ + struct nfs4_slot_table bc_slot_table; struct nfs_client *clp; }; -- cgit v1.2.3 From 963891ac43ecf9974d82f4c178752e11e007cf87 Mon Sep 17 00:00:00 2001 From: Ricardo Labiaga Date: Wed, 1 Apr 2009 09:23:34 -0400 Subject: nfs41: Backchannel: New find_client_with_session() Finds the 'struct nfs_client' that matches the server's address, major version number, and session ID. Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- fs/nfs/callback_proc.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index f731bbe7ac6..6b342e82ebb 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -104,6 +104,42 @@ out: #if defined(CONFIG_NFS_V4_1) +/* + * Returns a pointer to a held 'struct nfs_client' that matches the server's + * address, major version number, and session ID. It is the caller's + * responsibility to release the returned reference. + * + * Returns NULL if there are no connections with sessions, or if no session + * matches the one of interest. + */ + static struct nfs_client *find_client_with_session( + const struct sockaddr *addr, u32 nfsversion, + struct nfs4_sessionid *sessionid) +{ + struct nfs_client *clp; + + clp = nfs_find_client(addr, 4); + if (clp == NULL) + return NULL; + + do { + struct nfs_client *prev = clp; + + if (clp->cl_session != NULL) { + if (memcmp(clp->cl_session->sess_id.data, + sessionid->data, + NFS4_MAX_SESSIONID_LEN) == 0) { + /* Returns a held reference to clp */ + return clp; + } + } + clp = nfs_find_client_next(prev); + nfs_put_client(prev); + } while (clp != NULL); + + return NULL; +} + /* FIXME: validate args->cbs_{sequence,slot}id */ /* FIXME: referring calls should be processed */ unsigned nfs4_callback_sequence(struct cb_sequenceargs *args, -- cgit v1.2.3 From 68f3f90133d56e0c38f04f991e662c2b21592b31 Mon Sep 17 00:00:00 2001 From: Ricardo Labiaga Date: Wed, 1 Apr 2009 09:23:35 -0400 Subject: nfs41: Backchannel: CB_SEQUENCE validation Validates the callback's sessionID, the slot number, and the sequence ID. Increments the slot's sequence. Detects replays, but simply prints a debug message (if debugging is enabled since we don't yet implement a duplicate request cache for the backchannel. This should not present a problem, since only idempotent callbacks are currently implemented. Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy [nfs41: Backchannel: Be more obvious about the return value] [nfs41: Backchannel: dprink in host order] Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy --- fs/nfs/callback_proc.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 4 deletions(-) diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index 6b342e82ebb..b7da1f54da6 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -104,6 +104,57 @@ out: #if defined(CONFIG_NFS_V4_1) +/* + * Validate the sequenceID sent by the server. + * Return success if the sequenceID is one more than what we last saw on + * this slot, accounting for wraparound. Increments the slot's sequence. + * + * We don't yet implement a duplicate request cache, so at this time + * we will log replays, and process them as if we had not seen them before, + * but we don't bump the sequence in the slot. Not too worried about it, + * since we only currently implement idempotent callbacks anyway. + * + * We have a single slot backchannel at this time, so we don't bother + * checking the used_slots bit array on the table. The lower layer guarantees + * a single outstanding callback request at a time. + */ +static int +validate_seqid(struct nfs4_slot_table *tbl, u32 slotid, u32 seqid) +{ + struct nfs4_slot *slot; + + dprintk("%s enter. slotid %d seqid %d\n", + __func__, slotid, seqid); + + if (slotid > NFS41_BC_MAX_CALLBACKS) + return htonl(NFS4ERR_BADSLOT); + + slot = tbl->slots + slotid; + dprintk("%s slot table seqid: %d\n", __func__, slot->seq_nr); + + /* Normal */ + if (likely(seqid == slot->seq_nr + 1)) { + slot->seq_nr++; + return htonl(NFS4_OK); + } + + /* Replay */ + if (seqid == slot->seq_nr) { + dprintk("%s seqid %d is a replay - no DRC available\n", + __func__, seqid); + return htonl(NFS4_OK); + } + + /* Wraparound */ + if (seqid == 1 && (slot->seq_nr + 1) == 0) { + slot->seq_nr = 1; + return htonl(NFS4_OK); + } + + /* Misordered request */ + return htonl(NFS4ERR_SEQ_MISORDERED); +} + /* * Returns a pointer to a held 'struct nfs_client' that matches the server's * address, major version number, and session ID. It is the caller's @@ -140,18 +191,27 @@ out: return NULL; } -/* 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; + struct nfs_client *clp; + int i, status; for (i = 0; i < args->csa_nrclists; i++) kfree(args->csa_rclists[i].rcl_refcalls); kfree(args->csa_rclists); + status = htonl(NFS4ERR_BADSESSION); + clp = find_client_with_session(args->csa_addr, 4, &args->csa_sessionid); + if (clp == NULL) + goto out; + + status = validate_seqid(&clp->cl_session->bc_slot_table, + args->csa_slotid, args->csa_sequenceid); + if (status) + goto out_putclient; + memcpy(&res->csr_sessionid, &args->csa_sessionid, sizeof(res->csr_sessionid)); res->csr_sequenceid = args->csa_sequenceid; @@ -159,9 +219,12 @@ unsigned nfs4_callback_sequence(struct cb_sequenceargs *args, res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; res->csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; +out_putclient: + nfs_put_client(clp); +out: dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); res->csr_status = status; - return status; + return res->csr_status; } #endif /* CONFIG_NFS_V4_1 */ -- cgit v1.2.3 From bf40d3435caf49369058b1ed6bbc92f8e2bd92f1 Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Wed, 17 Jun 2009 18:02:09 -0700 Subject: NFS: add support for splice writes Adds support for splice writes. It effectively calls generic_file_splice_write() to do the writes. We need not worry about O_APPEND case as the combination of splice() writes and O_APPEND is disallowed. This patch propagates NFS write errors back to the caller. The number of bytes written via splice are being added to NFSIO_NORMALWRITTENBYTES as these are effectively cached writes. Signed-off-by: Suresh Jayaraman Signed-off-by: Trond Myklebust --- fs/nfs/file.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 52ed5676258..0055b813ec2 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -48,6 +48,9 @@ static ssize_t nfs_file_splice_read(struct file *filp, loff_t *ppos, size_t count, unsigned int flags); static ssize_t nfs_file_read(struct kiocb *, const struct iovec *iov, unsigned long nr_segs, loff_t pos); +static ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe, + struct file *filp, loff_t *ppos, + size_t count, unsigned int flags); static ssize_t nfs_file_write(struct kiocb *, const struct iovec *iov, unsigned long nr_segs, loff_t pos); static int nfs_file_flush(struct file *, fl_owner_t id); @@ -73,6 +76,7 @@ const struct file_operations nfs_file_operations = { .lock = nfs_lock, .flock = nfs_flock, .splice_read = nfs_file_splice_read, + .splice_write = nfs_file_splice_write, .check_flags = nfs_check_flags, .setlease = nfs_setlease, }; @@ -587,6 +591,33 @@ out_swapfile: goto out; } +static ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe, + struct file *filp, loff_t *ppos, + size_t count, unsigned int flags) +{ + struct dentry *dentry = filp->f_path.dentry; + struct inode *inode = dentry->d_inode; + ssize_t ret; + + dprintk("NFS splice_write(%s/%s, %lu@%llu)\n", + dentry->d_parent->d_name.name, dentry->d_name.name, + (unsigned long) count, (unsigned long long) *ppos); + + /* + * The combination of splice and an O_APPEND destination is disallowed. + */ + + nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, count); + + ret = generic_file_splice_write(pipe, filp, ppos, count, flags); + if (ret >= 0 && nfs_need_sync_write(filp, inode)) { + int err = nfs_do_fsync(nfs_file_open_context(filp), inode); + if (err < 0) + ret = err; + } + return ret; +} + static int do_getlk(struct file *filp, int cmd, struct file_lock *fl) { struct inode *inode = filp->f_mapping->host; -- cgit v1.2.3 From c381ad2cf2d5dcd3991bcc8a18fddd9d5c66ccaa Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 17 Jun 2009 18:02:09 -0700 Subject: NFS: Do not display the setting of the "intr" mount option The "intr" mount option has been deprecated for a while, but /proc/mounts continues to display "nointr" whether "intr" or "nointr" has been specified for a mount point. Since these options do not have any effect, simply do not display them. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/super.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 98b47d17740..539a61a0887 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -514,7 +514,6 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, const char *nostr; } nfs_info[] = { { NFS_MOUNT_SOFT, ",soft", ",hard" }, - { NFS_MOUNT_INTR, ",intr", ",nointr" }, { NFS_MOUNT_POSIX, ",posix", "" }, { NFS_MOUNT_NOCTO, ",nocto", "" }, { NFS_MOUNT_NOAC, ",noac", "" }, -- cgit v1.2.3 From a21bdd9b960ccce421b63aa0e3efda4fcdc26f10 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 17 Jun 2009 18:02:10 -0700 Subject: NFS: Return error code from nfs_callback_up() to user space If the kernel cannot start the NFSv4 callback service during a mount request, it returns -ENOMEM to user space, resulting in this message: mount.nfs4: Cannot allocate memory Adjust nfs_alloc_client() and nfs_get_client() to pass NFSv4 callback start-up errors back to user space so a less mysterious error message can be displayed by the mount command. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 75c9cd2aa11..0e9a1f9743b 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -114,6 +114,7 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_ { struct nfs_client *clp; struct rpc_cred *cred; + int err = -ENOMEM; if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) goto error_0; @@ -121,7 +122,8 @@ 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) + err = nfs_callback_up(); + if (err < 0) goto error_2; __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state); } @@ -133,6 +135,7 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_ clp->cl_addrlen = cl_init->addrlen; if (cl_init->hostname) { + err = -ENOMEM; clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL); if (!clp->cl_hostname) goto error_3; @@ -165,7 +168,7 @@ error_3: error_2: kfree(clp); error_0: - return NULL; + return ERR_PTR(err); } static void nfs4_shutdown_client(struct nfs_client *clp) @@ -456,9 +459,10 @@ static struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_in spin_unlock(&nfs_client_lock); new = nfs_alloc_client(cl_init); - } while (new); + } while (!IS_ERR(new)); - return ERR_PTR(-ENOMEM); + dprintk("--> nfs_get_client() = %ld [failed]\n", PTR_ERR(new)); + return new; /* install a new client and return with it unready */ install_client: -- cgit v1.2.3 From 18fc31641925867c871bc75270ce642c039188d3 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 17 Jun 2009 18:02:10 -0700 Subject: NFS: Fix false error return from nfs_callback_up() if ipv6.ko is not available Clear "ret" if the error return from svc_create_xprt(AF_INET6) was -EAFNOSUPORT. Otherwise, callback start-up will succeed, but nfs_callback_up() will return -EAFNOSUPPORT anyway, and the first NFSv4 mount attempt after a reboot will fail. Bug introduced by commit f738f517 in 2.6.30-rc1. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/callback.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index a886e692ddd..6e3bd42c21c 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -127,7 +127,9 @@ int nfs_callback_up(void) nfs_callback_tcpport6 = ret; dprintk("NFS: Callback listener port = %u (af %u)\n", nfs_callback_tcpport6, PF_INET6); - } else if (ret != -EAFNOSUPPORT) + } else if (ret == -EAFNOSUPPORT) + ret = 0; + else goto out_err; #endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ -- cgit v1.2.3 From 6c9dc4255108bab4ef5c177d369b99c3c23492a7 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 17 Jun 2009 18:02:10 -0700 Subject: lockd: Update NSM state from SM_MON replies When rpc.statd starts up in user space at boot time, it attempts to write the latest NSM local state number into /proc/sys/fs/nfs/nsm_local_state. If lockd.ko isn't loaded yet (as is the case in most configurations), that file doesn't exist, thus the kernel's NSM state remains set to its initial value of zero during lockd operation. This is a problem because rpc.statd and lockd use the NSM state number to prevent repeated lock recovery on rebooted hosts. If lockd sends a zero NSM state, but then a delayed SM_NOTIFY with a real NSM state number is received, there is no way for lockd or rpc.statd to distinguish that stale SM_NOTIFY from an actual reboot. Thus lock recovery could be performed after the rebooted host has already started reclaiming locks, and those locks will be lost. We could change /etc/init.d/nfslock so it always modprobes lockd.ko before starting rpc.statd. However, if lockd.ko is ever unloaded and reloaded, we are back at square one, since the NSM state is not preserved across an unload/reload cycle. This may happen frequently on clients that use automounter. A period of NFS inactivity causes lockd.ko to be unloaded, and the kernel loses its NSM state setting. Instead, let's use the fact that rpc.statd plants the local system's NSM state in every SM_MON (and SM_UNMON) reply. lockd performs a synchronous SM_MON upcall to the local rpc.statd _before_ sending its first NLM request to a new remote. This would permit rpc.statd to provide the current NSM state to lockd, even after lockd.ko had been unloaded and reloaded. Note that NLMPROC_LOCK arguments are constructed before the nsm_monitor() call, so we have to rearrange argument construction very slightly to make this all work out. And, the kernel appears to treat NSM state as a u32 (see struct nlm_args and nsm_res). Make nsm_local_state a u32 as well, to ensure we don't get bogus comparison results. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/lockd/clntproc.c | 2 +- fs/lockd/mon.c | 18 ++++++++++++------ include/linux/lockd/lockd.h | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index 273e229353f..f2fdcbce143 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -126,7 +126,6 @@ static void nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl) struct nlm_lock *lock = &argp->lock; nlmclnt_next_cookie(&argp->cookie); - argp->state = nsm_local_state; memcpy(&lock->fh, NFS_FH(fl->fl_file->f_path.dentry->d_inode), sizeof(struct nfs_fh)); lock->caller = utsname()->nodename; lock->oh.data = req->a_owner; @@ -521,6 +520,7 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) if (nsm_monitor(host) < 0) goto out; + req->a_args.state = nsm_local_state; fl->fl_flags |= FL_ACCESS; status = do_vfs_lock(fl); diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 6d5d4a4169e..38385336614 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -53,7 +53,7 @@ static DEFINE_SPINLOCK(nsm_lock); /* * Local NSM state */ -int __read_mostly nsm_local_state; +u32 __read_mostly nsm_local_state; int __read_mostly nsm_use_hostnames; static inline struct sockaddr *nsm_addr(const struct nsm_handle *nsm) @@ -184,13 +184,19 @@ int nsm_monitor(const struct nlm_host *host) nsm->sm_mon_name = nsm_use_hostnames ? nsm->sm_name : nsm->sm_addrbuf; status = nsm_mon_unmon(nsm, NSMPROC_MON, &res); - if (res.status != 0) + if (unlikely(res.status != 0)) status = -EIO; - if (status < 0) + if (unlikely(status < 0)) { printk(KERN_NOTICE "lockd: cannot monitor %s\n", nsm->sm_name); - else - nsm->sm_monitored = 1; - return status; + return status; + } + + nsm->sm_monitored = 1; + if (unlikely(nsm_local_state != res.state)) { + nsm_local_state = res.state; + dprintk("lockd: NSM state changed to %d\n", nsm_local_state); + } + return 0; } /** diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 51855dfd8ad..c325b187966 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -195,7 +195,7 @@ extern struct svc_procedure nlmsvc_procedures4[]; extern int nlmsvc_grace_period; extern unsigned long nlmsvc_timeout; extern int nsm_use_hostnames; -extern int nsm_local_state; +extern u32 nsm_local_state; /* * Lockd client functions -- cgit v1.2.3 From 0e5c2632e1c9182f0dadc31bec68d6f42e7905ea Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 17 Jun 2009 18:02:11 -0700 Subject: lockd: Don't bother with RPC ping for NSM upcalls Cut NSM upcall RPC traffic in half -- don't do a NULL call first. The cases where a ping would be helpful are rare. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/lockd/mon.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 38385336614..7fce1b52584 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -112,6 +112,7 @@ static struct rpc_clnt *nsm_create(void) .program = &nsm_program, .version = NSM_VERSION, .authflavor = RPC_AUTH_NULL, + .flags = RPC_CLNT_CREATE_NOPING, }; return rpc_create(&args); -- cgit v1.2.3 From 2ad780978b7c0c3e7877949f098cbd06e7c73839 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 17 Jun 2009 18:02:11 -0700 Subject: NFS: Clean up MNT program definitions Clean up: Relocate MNT program procedure number definitions to the only file that uses them. Relocate the version number definitions, which are shared, to nfs.h. Remove duplicate program number definitions. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/mount_clnt.c | 32 ++++++++++++++++++++++++++++---- fs/nfs/nfsroot.c | 3 +++ include/linux/nfs.h | 5 +++-- include/linux/nfs2.h | 7 ------- include/linux/nfs3.h | 5 ----- 5 files changed, 34 insertions(+), 18 deletions(-) diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c index ca905a5bb1b..af45a374d56 100644 --- a/fs/nfs/mount_clnt.c +++ b/fs/nfs/mount_clnt.c @@ -20,6 +20,30 @@ # define NFSDBG_FACILITY NFSDBG_MOUNT #endif +/* + * Defined by RFC 1094, section A.5 + */ +enum { + MOUNTPROC_NULL = 0, + MOUNTPROC_MNT = 1, + MOUNTPROC_DUMP = 2, + MOUNTPROC_UMNT = 3, + MOUNTPROC_UMNTALL = 4, + MOUNTPROC_EXPORT = 5, +}; + +/* + * Defined by RFC 1813, section 5.2 + */ +enum { + MOUNTPROC3_NULL = 0, + MOUNTPROC3_MNT = 1, + MOUNTPROC3_DUMP = 2, + MOUNTPROC3_UMNT = 3, + MOUNTPROC3_UMNTALL = 4, + MOUNTPROC3_EXPORT = 5, +}; + static struct rpc_program mnt_program; struct mnt_fhstatus { @@ -68,7 +92,7 @@ int nfs_mount(struct nfs_mount_request *info) if (info->version == NFS_MNT3_VERSION) msg.rpc_proc = &mnt_clnt->cl_procinfo[MOUNTPROC3_MNT]; else - msg.rpc_proc = &mnt_clnt->cl_procinfo[MNTPROC_MNT]; + msg.rpc_proc = &mnt_clnt->cl_procinfo[MOUNTPROC_MNT]; status = rpc_call_sync(mnt_clnt, &msg, 0); rpc_shutdown_client(mnt_clnt); @@ -145,13 +169,13 @@ static int xdr_decode_fhstatus3(struct rpc_rqst *req, __be32 *p, #define MNT_fhstatus3_sz (1 + 16) static struct rpc_procinfo mnt_procedures[] = { - [MNTPROC_MNT] = { - .p_proc = MNTPROC_MNT, + [MOUNTPROC_MNT] = { + .p_proc = MOUNTPROC_MNT, .p_encode = (kxdrproc_t) xdr_encode_dirpath, .p_decode = (kxdrproc_t) xdr_decode_fhstatus, .p_arglen = MNT_dirpath_sz, .p_replen = MNT_fhstatus_sz, - .p_statidx = MNTPROC_MNT, + .p_statidx = MOUNTPROC_MNT, .p_name = "MOUNT", }, }; diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index e3ed5908820..24c1b93874c 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c @@ -92,6 +92,9 @@ #undef NFSROOT_DEBUG #define NFSDBG_FACILITY NFSDBG_ROOT +/* Default port to use if server is not running a portmapper */ +#define NFS_MNT_PORT 627 + /* Default path we try to mount. "%s" gets replaced by our IP address */ #define NFS_ROOT "/tftpboot/%s" diff --git a/include/linux/nfs.h b/include/linux/nfs.h index 214d499718f..f387919bbc5 100644 --- a/include/linux/nfs.h +++ b/include/linux/nfs.h @@ -25,8 +25,9 @@ #define NFSMODE_SOCK 0140000 #define NFSMODE_FIFO 0010000 -#define NFS_MNT_PROGRAM 100005 -#define NFS_MNT_PORT 627 +#define NFS_MNT_PROGRAM 100005 +#define NFS_MNT_VERSION 1 +#define NFS_MNT3_VERSION 3 /* * NFS stats. The good thing with these values is that NFSv3 errors are diff --git a/include/linux/nfs2.h b/include/linux/nfs2.h index 0ed9517138f..fde24b30cc9 100644 --- a/include/linux/nfs2.h +++ b/include/linux/nfs2.h @@ -64,11 +64,4 @@ struct nfs2_fh { #define NFSPROC_READDIR 16 #define NFSPROC_STATFS 17 -#define NFS_MNT_PROGRAM 100005 -#define NFS_MNT_VERSION 1 -#define MNTPROC_NULL 0 -#define MNTPROC_MNT 1 -#define MNTPROC_UMNT 3 -#define MNTPROC_UMNTALL 4 - #endif /* _LINUX_NFS2_H */ diff --git a/include/linux/nfs3.h b/include/linux/nfs3.h index 539f3b550ea..ac33806ec7f 100644 --- a/include/linux/nfs3.h +++ b/include/linux/nfs3.h @@ -88,12 +88,7 @@ struct nfs3_fh { #define NFS3PROC_PATHCONF 20 #define NFS3PROC_COMMIT 21 -#define NFS_MNT3_PROGRAM 100005 #define NFS_MNT3_VERSION 3 -#define MOUNTPROC3_NULL 0 -#define MOUNTPROC3_MNT 1 -#define MOUNTPROC3_UMNT 3 -#define MOUNTPROC3_UMNTALL 4 #if defined(__KERNEL__) -- cgit v1.2.3 From 29a1bd6bf8c7a9ff511aaaf7e40fd6ca5198babe Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 17 Jun 2009 18:02:11 -0700 Subject: NFS: Use xdr_stream-based XDR encoder for MNT's dirpath argument Check the length of the supplied dirpath, and see that it fits properly in the RPC buffer. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/mount_clnt.c | 49 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c index af45a374d56..93361afcd5d 100644 --- a/fs/nfs/mount_clnt.c +++ b/fs/nfs/mount_clnt.c @@ -20,6 +20,21 @@ # define NFSDBG_FACILITY NFSDBG_MOUNT #endif +/* + * Defined by RFC 1094, section A.3; and RFC 1813, section 5.1.4 + */ +#define MNTPATHLEN (1024) + +/* + * XDR data type sizes + */ +#define encode_dirpath_sz (1 + XDR_QUADLEN(MNTPATHLEN)) + +/* + * XDR argument and result sizes + */ +#define MNT_enc_dirpath_sz encode_dirpath_sz + /* * Defined by RFC 1094, section A.5 */ @@ -135,6 +150,31 @@ static int xdr_encode_dirpath(struct rpc_rqst *req, __be32 *p, return 0; } +static int encode_mntdirpath(struct xdr_stream *xdr, const char *pathname) +{ + const u32 pathname_len = strlen(pathname); + __be32 *p; + + if (unlikely(pathname_len > MNTPATHLEN)) + return -EIO; + + p = xdr_reserve_space(xdr, sizeof(u32) + pathname_len); + if (unlikely(p == NULL)) + return -EIO; + xdr_encode_opaque(p, pathname, pathname_len); + + return 0; +} + +static int mnt_enc_dirpath(struct rpc_rqst *req, __be32 *p, + const char *dirpath) +{ + struct xdr_stream xdr; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + return encode_mntdirpath(&xdr, dirpath); +} + static int xdr_decode_fhstatus(struct rpc_rqst *req, __be32 *p, struct mnt_fhstatus *res) { @@ -164,16 +204,15 @@ static int xdr_decode_fhstatus3(struct rpc_rqst *req, __be32 *p, return 0; } -#define MNT_dirpath_sz (1 + 256) #define MNT_fhstatus_sz (1 + 8) #define MNT_fhstatus3_sz (1 + 16) static struct rpc_procinfo mnt_procedures[] = { [MOUNTPROC_MNT] = { .p_proc = MOUNTPROC_MNT, - .p_encode = (kxdrproc_t) xdr_encode_dirpath, + .p_encode = (kxdrproc_t)mnt_enc_dirpath, .p_decode = (kxdrproc_t) xdr_decode_fhstatus, - .p_arglen = MNT_dirpath_sz, + .p_arglen = MNT_enc_dirpath_sz, .p_replen = MNT_fhstatus_sz, .p_statidx = MOUNTPROC_MNT, .p_name = "MOUNT", @@ -183,9 +222,9 @@ static struct rpc_procinfo mnt_procedures[] = { static struct rpc_procinfo mnt3_procedures[] = { [MOUNTPROC3_MNT] = { .p_proc = MOUNTPROC3_MNT, - .p_encode = (kxdrproc_t) xdr_encode_dirpath, + .p_encode = (kxdrproc_t)mnt_enc_dirpath, .p_decode = (kxdrproc_t) xdr_decode_fhstatus3, - .p_arglen = MNT_dirpath_sz, + .p_arglen = MNT_enc_dirpath_sz, .p_replen = MNT_fhstatus3_sz, .p_statidx = MOUNTPROC3_MNT, .p_name = "MOUNT", -- cgit v1.2.3 From 99835db430904e90c0640ebc6b91cd2a90a118f7 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 17 Jun 2009 18:02:11 -0700 Subject: NFS: remove unused function in fs/nfs/mount_clnt.c Clean up: remove xdr_encode_dirpath() now that it has been replaced. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/mount_clnt.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c index 93361afcd5d..79b5954b8a1 100644 --- a/fs/nfs/mount_clnt.c +++ b/fs/nfs/mount_clnt.c @@ -141,14 +141,6 @@ out_mnt_err: /* * XDR encode/decode functions for MOUNT */ -static int xdr_encode_dirpath(struct rpc_rqst *req, __be32 *p, - const char *path) -{ - p = xdr_encode_string(p, path); - - req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); - return 0; -} static int encode_mntdirpath(struct xdr_stream *xdr, const char *pathname) { -- cgit v1.2.3 From fb12529577541aa02f9c3d9e325329f9568dfb58 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 17 Jun 2009 18:02:12 -0700 Subject: NFS: Add separate mountd status code decoders for each mountd version Introduce data structures and xdr_stream-based decoding functions for unmarshalling mountd status codes properly. Mountd version 3 uses specific standard error return codes that are not errno values and not NFS3ERR_ values. These have a well-defined standard mapping to local errno values. Introduce data structures and a decoder function that map these status codes to local errno values properly. This is new functionality (but not used yet). Version 1 mountd status values are defined by RFC 1094 as UNIX error values (errno values). Errno values on heterogeneous systems do not necessarily match each other. To avoid exposing possibly incorrect errno values to upper layers, the current XDR decoder converts all non-zero MNT version 1 status codes to -EACCES. The OpenGroup XNFS standard provides a mapping similar to but smaller than the version 3 error codes. Implement a decoder that uses the XNFS error codes, replacing the current decoder. For both mountd protocol versions, map unrecognized errors to -EACCES. Finally we introduce a replacement data structure for mnt_fhstatus at this time, which is used by the new XDR decoders. In addition to documenting that the status value returned by the XDR decoders is always an errno, this new structure will be expanded in subsequent patches. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/mount_clnt.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c index 79b5954b8a1..8429885bc72 100644 --- a/fs/nfs/mount_clnt.c +++ b/fs/nfs/mount_clnt.c @@ -29,6 +29,8 @@ * XDR data type sizes */ #define encode_dirpath_sz (1 + XDR_QUADLEN(MNTPATHLEN)) +#define MNT_status_sz (1) +#define MNT_fhs_status_sz (1) /* * XDR argument and result sizes @@ -61,6 +63,65 @@ enum { static struct rpc_program mnt_program; +/* + * Defined by OpenGroup XNFS Version 3W, chapter 8 + */ +enum mountstat { + MNT_OK = 0, + MNT_EPERM = 1, + MNT_ENOENT = 2, + MNT_EACCES = 13, + MNT_EINVAL = 22, +}; + +static struct { + u32 status; + int errno; +} mnt_errtbl[] = { + { .status = MNT_OK, .errno = 0, }, + { .status = MNT_EPERM, .errno = -EPERM, }, + { .status = MNT_ENOENT, .errno = -ENOENT, }, + { .status = MNT_EACCES, .errno = -EACCES, }, + { .status = MNT_EINVAL, .errno = -EINVAL, }, +}; + +/* + * Defined by RFC 1813, section 5.1.5 + */ +enum mountstat3 { + MNT3_OK = 0, /* no error */ + MNT3ERR_PERM = 1, /* Not owner */ + MNT3ERR_NOENT = 2, /* No such file or directory */ + MNT3ERR_IO = 5, /* I/O error */ + MNT3ERR_ACCES = 13, /* Permission denied */ + MNT3ERR_NOTDIR = 20, /* Not a directory */ + MNT3ERR_INVAL = 22, /* Invalid argument */ + MNT3ERR_NAMETOOLONG = 63, /* Filename too long */ + MNT3ERR_NOTSUPP = 10004, /* Operation not supported */ + MNT3ERR_SERVERFAULT = 10006, /* A failure on the server */ +}; + +static struct { + u32 status; + int errno; +} mnt3_errtbl[] = { + { .status = MNT3_OK, .errno = 0, }, + { .status = MNT3ERR_PERM, .errno = -EPERM, }, + { .status = MNT3ERR_NOENT, .errno = -ENOENT, }, + { .status = MNT3ERR_IO, .errno = -EIO, }, + { .status = MNT3ERR_ACCES, .errno = -EACCES, }, + { .status = MNT3ERR_NOTDIR, .errno = -ENOTDIR, }, + { .status = MNT3ERR_INVAL, .errno = -EINVAL, }, + { .status = MNT3ERR_NAMETOOLONG, .errno = -ENAMETOOLONG, }, + { .status = MNT3ERR_NOTSUPP, .errno = -ENOTSUPP, }, + { .status = MNT3ERR_SERVERFAULT, .errno = -ESERVERFAULT, }, +}; + +struct mountres { + int errno; + struct nfs_fh *fh; +}; + struct mnt_fhstatus { u32 status; struct nfs_fh *fh; @@ -179,6 +240,61 @@ static int xdr_decode_fhstatus(struct rpc_rqst *req, __be32 *p, return 0; } +/* + * RFC 1094: "A non-zero status indicates some sort of error. In this + * case, the status is a UNIX error number." This can be problematic + * if the server and client use different errno values for the same + * error. + * + * However, the OpenGroup XNFS spec provides a simple mapping that is + * independent of local errno values on the server and the client. + */ +static int decode_status(struct xdr_stream *xdr, struct mountres *res) +{ + unsigned int i; + u32 status; + __be32 *p; + + p = xdr_inline_decode(xdr, sizeof(status)); + if (unlikely(p == NULL)) + return -EIO; + status = ntohl(*p); + + for (i = 0; i <= ARRAY_SIZE(mnt_errtbl); i++) { + if (mnt_errtbl[i].status == status) { + res->errno = mnt_errtbl[i].errno; + return 0; + } + } + + dprintk("NFS: unrecognized MNT status code: %u\n", status); + res->errno = -EACCES; + return 0; +} + +static int decode_fhs_status(struct xdr_stream *xdr, struct mountres *res) +{ + unsigned int i; + u32 status; + __be32 *p; + + p = xdr_inline_decode(xdr, sizeof(status)); + if (unlikely(p == NULL)) + return -EIO; + status = ntohl(*p); + + for (i = 0; i <= ARRAY_SIZE(mnt3_errtbl); i++) { + if (mnt3_errtbl[i].status == status) { + res->errno = mnt3_errtbl[i].errno; + return 0; + } + } + + dprintk("NFS: unrecognized MNT3 status code: %u\n", status); + res->errno = -EACCES; + return 0; +} + static int xdr_decode_fhstatus3(struct rpc_rqst *req, __be32 *p, struct mnt_fhstatus *res) { -- cgit v1.2.3 From 4fdcd9966d8469be26a6f12122ac21ffce19fc20 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 17 Jun 2009 18:02:12 -0700 Subject: NFS: add new file handle decoders to in-kernel mountd client Introduce xdr_stream-based XDR file handle decoders to the in-kernel mountd client. These are more careful than the existing decoder functions about buffer overflows and data type and range checking. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/mount_clnt.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c index 8429885bc72..dd8c6f448ea 100644 --- a/fs/nfs/mount_clnt.c +++ b/fs/nfs/mount_clnt.c @@ -31,6 +31,8 @@ #define encode_dirpath_sz (1 + XDR_QUADLEN(MNTPATHLEN)) #define MNT_status_sz (1) #define MNT_fhs_status_sz (1) +#define MNT_fhandle_sz XDR_QUADLEN(NFS2_FHSIZE) +#define MNT_fhandle3_sz (1 + XDR_QUADLEN(NFS3_FHSIZE)) /* * XDR argument and result sizes @@ -272,6 +274,20 @@ static int decode_status(struct xdr_stream *xdr, struct mountres *res) return 0; } +static int decode_fhandle(struct xdr_stream *xdr, struct mountres *res) +{ + struct nfs_fh *fh = res->fh; + __be32 *p; + + p = xdr_inline_decode(xdr, NFS2_FHSIZE); + if (unlikely(p == NULL)) + return -EIO; + + fh->size = NFS2_FHSIZE; + memcpy(fh->data, p, NFS2_FHSIZE); + return 0; +} + static int decode_fhs_status(struct xdr_stream *xdr, struct mountres *res) { unsigned int i; @@ -295,6 +311,29 @@ static int decode_fhs_status(struct xdr_stream *xdr, struct mountres *res) return 0; } +static int decode_fhandle3(struct xdr_stream *xdr, struct mountres *res) +{ + struct nfs_fh *fh = res->fh; + u32 size; + __be32 *p; + + p = xdr_inline_decode(xdr, sizeof(size)); + if (unlikely(p == NULL)) + return -EIO; + + size = ntohl(*p++); + if (size > NFS3_FHSIZE || size == 0) + return -EIO; + + p = xdr_inline_decode(xdr, size); + if (unlikely(p == NULL)) + return -EIO; + + fh->size = size; + memcpy(fh->data, p, size); + return 0; +} + static int xdr_decode_fhstatus3(struct rpc_rqst *req, __be32 *p, struct mnt_fhstatus *res) { -- cgit v1.2.3 From a14017db2852f9393a401a0f64053c331003babf Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 17 Jun 2009 18:02:12 -0700 Subject: NFS: add XDR decoder for mountd version 3 auth-flavor lists Introduce an xdr_stream-based XDR decoder that can unpack the auth- flavor list returned in a MNT3 reply. The nfs_mount() function's caller allocates an array, and passes the size and a pointer to it. The decoder decodes all the flavors it can into the array, and returns the number of decoded flavors. If the caller is not interested in the auth flavors, it can pass a value of zero as the size of the pre-allocated array. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/internal.h | 6 ++++++ fs/nfs/mount_clnt.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index e4d6a8348ad..0207758de44 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -29,6 +29,12 @@ struct nfs_clone_mount { rpc_authflavor_t authflavor; }; +/* + * Note: RFC 1813 doesn't limit the number of auth flavors that + * a server can return, so make something up. + */ +#define NFS_MAX_SECFLAVORS (12) + /* * In-kernel mount arguments */ diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c index dd8c6f448ea..ed0a27ed538 100644 --- a/fs/nfs/mount_clnt.c +++ b/fs/nfs/mount_clnt.c @@ -33,6 +33,7 @@ #define MNT_fhs_status_sz (1) #define MNT_fhandle_sz XDR_QUADLEN(NFS2_FHSIZE) #define MNT_fhandle3_sz (1 + XDR_QUADLEN(NFS3_FHSIZE)) +#define MNT_authflav3_sz (1 + NFS_MAX_SECFLAVORS) /* * XDR argument and result sizes @@ -122,6 +123,8 @@ static struct { struct mountres { int errno; struct nfs_fh *fh; + unsigned int *auth_count; + rpc_authflavor_t *auth_flavors; }; struct mnt_fhstatus { @@ -334,6 +337,40 @@ static int decode_fhandle3(struct xdr_stream *xdr, struct mountres *res) return 0; } +static int decode_auth_flavors(struct xdr_stream *xdr, struct mountres *res) +{ + rpc_authflavor_t *flavors = res->auth_flavors; + unsigned int *count = res->auth_count; + u32 entries, i; + __be32 *p; + + if (*count == 0) + return 0; + + p = xdr_inline_decode(xdr, sizeof(entries)); + if (unlikely(p == NULL)) + return -EIO; + entries = ntohl(*p); + dprintk("NFS: received %u auth flavors\n", entries); + if (entries > NFS_MAX_SECFLAVORS) + entries = NFS_MAX_SECFLAVORS; + + p = xdr_inline_decode(xdr, sizeof(u32) * entries); + if (unlikely(p == NULL)) + return -EIO; + + if (entries > *count) + entries = *count; + + for (i = 0; i < entries; i++) { + flavors[i] = ntohl(*p++); + dprintk("NFS:\tflavor %u: %d\n", i, flavors[i]); + } + *count = i; + + return 0; +} + static int xdr_decode_fhstatus3(struct rpc_rqst *req, __be32 *p, struct mnt_fhstatus *res) { -- cgit v1.2.3 From 8e02f6b9aae9b265064f929c6df15222b9baf256 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 17 Jun 2009 18:02:13 -0700 Subject: NFS: Update MNT and MNT3 reply decoding functions Solder xdr_stream-based XDR decoding functions into the in-kernel mountd client that are more careful about checking data types and watching for buffer overflows. The new MNT3 decoder includes support for auth-flavor list decoding. The "_sz" macro for MNT3 replies was missing the size of the file handle. I've added this back, and included the size of the auth flavor array. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/internal.h | 2 ++ fs/nfs/mount_clnt.c | 63 +++++++++++++++++++++++++++++++++++++++++------------ fs/nfs/nfsroot.c | 2 ++ fs/nfs/super.c | 2 ++ 4 files changed, 55 insertions(+), 14 deletions(-) diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 0207758de44..10cf1110df4 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -83,6 +83,8 @@ struct nfs_mount_request { unsigned short protocol; struct nfs_fh *fh; int noresvport; + unsigned int *auth_flav_len; + rpc_authflavor_t *auth_flavs; }; extern int nfs_mount(struct nfs_mount_request *info); diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c index ed0a27ed538..618d815c309 100644 --- a/fs/nfs/mount_clnt.c +++ b/fs/nfs/mount_clnt.c @@ -39,6 +39,9 @@ * XDR argument and result sizes */ #define MNT_enc_dirpath_sz encode_dirpath_sz +#define MNT_dec_mountres_sz (MNT_status_sz + MNT_fhandle_sz) +#define MNT_dec_mountres3_sz (MNT_status_sz + MNT_fhandle_sz + \ + MNT_authflav3_sz) /* * Defined by RFC 1094, section A.5 @@ -140,8 +143,10 @@ struct mnt_fhstatus { */ int nfs_mount(struct nfs_mount_request *info) { - struct mnt_fhstatus result = { - .fh = info->fh + struct mountres result = { + .fh = info->fh, + .auth_count = info->auth_flav_len, + .auth_flavors = info->auth_flavs, }; struct rpc_message msg = { .rpc_argp = info->dirpath, @@ -180,7 +185,7 @@ int nfs_mount(struct nfs_mount_request *info) if (status < 0) goto out_call_err; - if (result.status != 0) + if (result.errno != 0) goto out_mnt_err; dprintk("NFS: MNT request succeeded\n"); @@ -191,16 +196,16 @@ out: out_clnt_err: status = PTR_ERR(mnt_clnt); - dprintk("NFS: failed to create RPC client, status=%d\n", status); + dprintk("NFS: failed to create MNT RPC client, status=%d\n", status); goto out; out_call_err: - dprintk("NFS: failed to start MNT request, status=%d\n", status); + dprintk("NFS: MNT request failed, status=%d\n", status); goto out; out_mnt_err: - dprintk("NFS: MNT server returned result %d\n", result.status); - status = nfs_stat_to_errno(result.status); + dprintk("NFS: MNT server returned result %d\n", result.errno); + status = result.errno; goto out; } @@ -291,6 +296,20 @@ static int decode_fhandle(struct xdr_stream *xdr, struct mountres *res) return 0; } +static int mnt_dec_mountres(struct rpc_rqst *req, __be32 *p, + struct mountres *res) +{ + struct xdr_stream xdr; + int status; + + xdr_init_decode(&xdr, &req->rq_rcv_buf, p); + + status = decode_status(&xdr, res); + if (unlikely(status != 0 || res->errno != 0)) + return status; + return decode_fhandle(&xdr, res); +} + static int decode_fhs_status(struct xdr_stream *xdr, struct mountres *res) { unsigned int i; @@ -371,6 +390,25 @@ static int decode_auth_flavors(struct xdr_stream *xdr, struct mountres *res) return 0; } +static int mnt_dec_mountres3(struct rpc_rqst *req, __be32 *p, + struct mountres *res) +{ + struct xdr_stream xdr; + int status; + + xdr_init_decode(&xdr, &req->rq_rcv_buf, p); + + status = decode_fhs_status(&xdr, res); + if (unlikely(status != 0 || res->errno != 0)) + return status; + status = decode_fhandle3(&xdr, res); + if (unlikely(status != 0)) { + res->errno = -EBADHANDLE; + return 0; + } + return decode_auth_flavors(&xdr, res); +} + static int xdr_decode_fhstatus3(struct rpc_rqst *req, __be32 *p, struct mnt_fhstatus *res) { @@ -388,16 +426,13 @@ static int xdr_decode_fhstatus3(struct rpc_rqst *req, __be32 *p, return 0; } -#define MNT_fhstatus_sz (1 + 8) -#define MNT_fhstatus3_sz (1 + 16) - static struct rpc_procinfo mnt_procedures[] = { [MOUNTPROC_MNT] = { .p_proc = MOUNTPROC_MNT, .p_encode = (kxdrproc_t)mnt_enc_dirpath, - .p_decode = (kxdrproc_t) xdr_decode_fhstatus, + .p_decode = (kxdrproc_t)mnt_dec_mountres, .p_arglen = MNT_enc_dirpath_sz, - .p_replen = MNT_fhstatus_sz, + .p_replen = MNT_dec_mountres_sz, .p_statidx = MOUNTPROC_MNT, .p_name = "MOUNT", }, @@ -407,9 +442,9 @@ static struct rpc_procinfo mnt3_procedures[] = { [MOUNTPROC3_MNT] = { .p_proc = MOUNTPROC3_MNT, .p_encode = (kxdrproc_t)mnt_enc_dirpath, - .p_decode = (kxdrproc_t) xdr_decode_fhstatus3, + .p_decode = (kxdrproc_t)mnt_dec_mountres3, .p_arglen = MNT_enc_dirpath_sz, - .p_replen = MNT_fhstatus3_sz, + .p_replen = MNT_dec_mountres3_sz, .p_statidx = MOUNTPROC3_MNT, .p_name = "MOUNT", }, diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index 24c1b93874c..8c55b27c0de 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c @@ -490,6 +490,7 @@ static int __init root_nfs_get_handle(void) { struct nfs_fh fh; struct sockaddr_in sin; + unsigned int auth_flav_len = 0; struct nfs_mount_request request = { .sap = (struct sockaddr *)&sin, .salen = sizeof(sin), @@ -499,6 +500,7 @@ static int __init root_nfs_get_handle(void) .protocol = (nfs_data.flags & NFS_MOUNT_TCP) ? XPRT_TRANSPORT_TCP : XPRT_TRANSPORT_UDP, .fh = &fh, + .auth_flav_len = &auth_flav_len, }; int status; diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 539a61a0887..0b72357cdc4 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -1380,6 +1380,7 @@ out_security_failure: static int nfs_try_mount(struct nfs_parsed_mount_data *args, struct nfs_fh *root_fh) { + unsigned int auth_flavor_len = 0; struct nfs_mount_request request = { .sap = (struct sockaddr *) &args->mount_server.address, @@ -1387,6 +1388,7 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args, .protocol = args->mount_server.protocol, .fh = root_fh, .noresvport = args->flags & NFS_MOUNT_NORESVPORT, + .auth_flav_len = &auth_flavor_len, }; int status; -- cgit v1.2.3 From 065015e5efff60884ad600a3e9a5127dbb684429 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 17 Jun 2009 18:02:13 -0700 Subject: NFS: Remove unused XDR decoder functions Clean up: Remove xdr_decode_fhstatus() and xdr_decode_fhstatus3(), now that they are unused. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/mount_clnt.c | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c index 618d815c309..38ef9eaec40 100644 --- a/fs/nfs/mount_clnt.c +++ b/fs/nfs/mount_clnt.c @@ -238,18 +238,6 @@ static int mnt_enc_dirpath(struct rpc_rqst *req, __be32 *p, return encode_mntdirpath(&xdr, dirpath); } -static int xdr_decode_fhstatus(struct rpc_rqst *req, __be32 *p, - struct mnt_fhstatus *res) -{ - struct nfs_fh *fh = res->fh; - - if ((res->status = ntohl(*p++)) == 0) { - fh->size = NFS2_FHSIZE; - memcpy(fh->data, p, NFS2_FHSIZE); - } - return 0; -} - /* * RFC 1094: "A non-zero status indicates some sort of error. In this * case, the status is a UNIX error number." This can be problematic @@ -409,23 +397,6 @@ static int mnt_dec_mountres3(struct rpc_rqst *req, __be32 *p, return decode_auth_flavors(&xdr, res); } -static int xdr_decode_fhstatus3(struct rpc_rqst *req, __be32 *p, - struct mnt_fhstatus *res) -{ - struct nfs_fh *fh = res->fh; - unsigned size; - - if ((res->status = ntohl(*p++)) == 0) { - size = ntohl(*p++); - if (size <= NFS3_FHSIZE && size != 0) { - fh->size = size; - memcpy(fh->data, p, size); - } else - res->status = -EBADHANDLE; - } - return 0; -} - static struct rpc_procinfo mnt_procedures[] = { [MOUNTPROC_MNT] = { .p_proc = MOUNTPROC_MNT, -- cgit v1.2.3 From d23c45fd84f79a3b84899dac053dcafe9d43ebc9 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 17 Jun 2009 18:02:13 -0700 Subject: NFS: Invalid mount option values should always fail, even with "sloppy" Ian Kent reports: "I've noticed a couple of other regressions with the options vers and proto option of mount.nfs(8). The commands: mount -t nfs -o vers= :/ / mount -t nfs -o proto= :/ / both immediately fail. But if the "-s" option is also used they both succeed with the mount falling back to defaults (by the look of it). In the past these failed even when the sloppy option was given, as I think they should. I believe the sloppy option is meant to allow the mount command to still function for mount options (for example in shared autofs maps) that exist on other Unix implementations but aren't present in the Linux mount.nfs(8). So, an invalid value specified for a known mount option is different to an unknown mount option and should fail appropriately." See RH bugzilla 486266. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/super.c | 157 ++++++++++++++++++++++----------------------------------- 1 file changed, 61 insertions(+), 96 deletions(-) diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 0b72357cdc4..a2b2805caf9 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -942,11 +942,6 @@ static int nfs_parse_security_flavors(char *value, return 1; } -static void nfs_parse_invalid_value(const char *option) -{ - dfprintk(MOUNT, "NFS: bad value specified for %s option\n", option); -} - /* * Error-check and convert a string of mount options from user space into * a data structure. The whole mount string is processed; bad options are @@ -957,7 +952,7 @@ static int nfs_parse_mount_options(char *raw, struct nfs_parsed_mount_data *mnt) { char *p, *string, *secdata; - int rc, sloppy = 0, errors = 0; + int rc, sloppy = 0, invalid_option = 0; if (!raw) { dfprintk(MOUNT, "NFS: mount options string was NULL.\n"); @@ -1091,113 +1086,82 @@ static int nfs_parse_mount_options(char *raw, */ case Opt_port: if (match_int(args, &option) || - option < 0 || option > USHORT_MAX) { - errors++; - nfs_parse_invalid_value("port"); - } else - mnt->nfs_server.port = option; + option < 0 || option > USHORT_MAX) + goto out_invalid_value; + mnt->nfs_server.port = option; break; case Opt_rsize: - if (match_int(args, &option) || option < 0) { - errors++; - nfs_parse_invalid_value("rsize"); - } else - mnt->rsize = option; + if (match_int(args, &option) || option < 0) + goto out_invalid_value; + mnt->rsize = option; break; case Opt_wsize: - if (match_int(args, &option) || option < 0) { - errors++; - nfs_parse_invalid_value("wsize"); - } else - mnt->wsize = option; + if (match_int(args, &option) || option < 0) + goto out_invalid_value; + mnt->wsize = option; break; case Opt_bsize: - if (match_int(args, &option) || option < 0) { - errors++; - nfs_parse_invalid_value("bsize"); - } else - mnt->bsize = option; + if (match_int(args, &option) || option < 0) + goto out_invalid_value; + mnt->bsize = option; break; case Opt_timeo: - if (match_int(args, &option) || option <= 0) { - errors++; - nfs_parse_invalid_value("timeo"); - } else - mnt->timeo = option; + if (match_int(args, &option) || option <= 0) + goto out_invalid_value; + mnt->timeo = option; break; case Opt_retrans: - if (match_int(args, &option) || option <= 0) { - errors++; - nfs_parse_invalid_value("retrans"); - } else - mnt->retrans = option; + if (match_int(args, &option) || option <= 0) + goto out_invalid_value; + mnt->retrans = option; break; case Opt_acregmin: - if (match_int(args, &option) || option < 0) { - errors++; - nfs_parse_invalid_value("acregmin"); - } else - mnt->acregmin = option; + if (match_int(args, &option) || option < 0) + goto out_invalid_value; + mnt->acregmin = option; break; case Opt_acregmax: - if (match_int(args, &option) || option < 0) { - errors++; - nfs_parse_invalid_value("acregmax"); - } else - mnt->acregmax = option; + if (match_int(args, &option) || option < 0) + goto out_invalid_value; + mnt->acregmax = option; break; case Opt_acdirmin: - if (match_int(args, &option) || option < 0) { - errors++; - nfs_parse_invalid_value("acdirmin"); - } else - mnt->acdirmin = option; + if (match_int(args, &option) || option < 0) + goto out_invalid_value; + mnt->acdirmin = option; break; case Opt_acdirmax: - if (match_int(args, &option) || option < 0) { - errors++; - nfs_parse_invalid_value("acdirmax"); - } else - mnt->acdirmax = option; + if (match_int(args, &option) || option < 0) + goto out_invalid_value; + mnt->acdirmax = option; break; case Opt_actimeo: - if (match_int(args, &option) || option < 0) { - errors++; - nfs_parse_invalid_value("actimeo"); - } else - mnt->acregmin = mnt->acregmax = - mnt->acdirmin = mnt->acdirmax = option; + if (match_int(args, &option) || option < 0) + goto out_invalid_value; + mnt->acregmin = mnt->acregmax = + mnt->acdirmin = mnt->acdirmax = option; break; case Opt_namelen: - if (match_int(args, &option) || option < 0) { - errors++; - nfs_parse_invalid_value("namlen"); - } else - mnt->namlen = option; + if (match_int(args, &option) || option < 0) + goto out_invalid_value; + mnt->namlen = option; break; case Opt_mountport: if (match_int(args, &option) || - option < 0 || option > USHORT_MAX) { - errors++; - nfs_parse_invalid_value("mountport"); - } else - mnt->mount_server.port = option; + option < 0 || option > USHORT_MAX) + goto out_invalid_value; + mnt->mount_server.port = option; break; case Opt_mountvers: if (match_int(args, &option) || option < NFS_MNT_VERSION || - option > NFS_MNT3_VERSION) { - errors++; - nfs_parse_invalid_value("mountvers"); - } else - mnt->mount_server.version = option; + option > NFS_MNT3_VERSION) + goto out_invalid_value; + mnt->mount_server.version = option; break; case Opt_nfsvers: - if (match_int(args, &option)) { - errors++; - nfs_parse_invalid_value("nfsvers"); - break; - } + if (match_int(args, &option)) + goto out_invalid_value; switch (option) { case NFS2_VERSION: mnt->flags &= ~NFS_MOUNT_VER3; @@ -1206,8 +1170,7 @@ static int nfs_parse_mount_options(char *raw, mnt->flags |= NFS_MOUNT_VER3; break; default: - errors++; - nfs_parse_invalid_value("nfsvers"); + goto out_invalid_value; } break; @@ -1221,9 +1184,9 @@ static int nfs_parse_mount_options(char *raw, rc = nfs_parse_security_flavors(string, mnt); kfree(string); if (!rc) { - errors++; dfprintk(MOUNT, "NFS: unrecognized " "security flavor\n"); + return 0; } break; case Opt_proto: @@ -1237,23 +1200,25 @@ static int nfs_parse_mount_options(char *raw, case Opt_xprt_udp: mnt->flags &= ~NFS_MOUNT_TCP; mnt->nfs_server.protocol = XPRT_TRANSPORT_UDP; + kfree(string); break; case Opt_xprt_tcp: mnt->flags |= NFS_MOUNT_TCP; mnt->nfs_server.protocol = XPRT_TRANSPORT_TCP; + kfree(string); break; case Opt_xprt_rdma: /* vector side protocols to TCP */ mnt->flags |= NFS_MOUNT_TCP; mnt->nfs_server.protocol = XPRT_TRANSPORT_RDMA; xprt_load_transport(string); + kfree(string); break; default: - errors++; dfprintk(MOUNT, "NFS: unrecognized " "transport protocol\n"); + return 0; } - kfree(string); break; case Opt_mountproto: string = match_strdup(args); @@ -1272,9 +1237,9 @@ static int nfs_parse_mount_options(char *raw, break; case Opt_xprt_rdma: /* not used for side protocols */ default: - errors++; dfprintk(MOUNT, "NFS: unrecognized " "transport protocol\n"); + return 0; } break; case Opt_addr: @@ -1330,9 +1295,9 @@ static int nfs_parse_mount_options(char *raw, mnt->flags |= NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE; break; default: - errors++; dfprintk(MOUNT, "NFS: invalid " "lookupcache argument\n"); + return 0; }; break; @@ -1350,20 +1315,20 @@ static int nfs_parse_mount_options(char *raw, break; default: - errors++; + invalid_option = 1; dfprintk(MOUNT, "NFS: unrecognized mount option " "'%s'\n", p); } } - if (errors > 0) { - dfprintk(MOUNT, "NFS: parsing encountered %d error%s\n", - errors, (errors == 1 ? "" : "s")); - if (!sloppy) - return 0; - } + if (!sloppy && invalid_option) + return 0; + return 1; +out_invalid_value: + printk(KERN_INFO "NFS: bad mount option value specified: %s \n", p); + return 0; out_nomem: printk(KERN_INFO "NFS: not enough memory to parse option\n"); return 0; -- cgit v1.2.3 From a5a16bae707cd5d2bc97d7bd1a30079f18113a77 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 17 Jun 2009 18:02:14 -0700 Subject: NFS: More "sloppy" parsing problems Specifying "port=-5" with the kernel's current mount option parser generates "unrecognized mount option". If "sloppy" is set, this causes the mount to succeed and use the default values; the desired behavior is that, since this is a valid option with an invalid value, the mount should fail, even with "sloppy." To properly handle "sloppy" parsing, we need to distinguish between correct options with invalid values, and incorrect options. We will need to parse integer values by hand, therefore, and not rely on match_token(). For instance, these must all fail with "invalid value": port=12345678 port=-5 port=samuel and not with "unrecognized option," as they do currently. Thus, for the sake of match_token() we need to treat the values for these options as strings, and do the conversion to integers using strict_strtol(). This is basically the same solution we used for the earlier "retry=" fix (commit ecbb3845), except in this case the kernel actually has to parse the value, rather than ignore it. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/super.c | 142 +++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 108 insertions(+), 34 deletions(-) diff --git a/fs/nfs/super.c b/fs/nfs/super.c index a2b2805caf9..b798ed1bd36 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -139,22 +139,22 @@ static const match_table_t nfs_mount_option_tokens = { { Opt_fscache_uniq, "fsc=%s" }, { Opt_nofscache, "nofsc" }, - { Opt_port, "port=%u" }, - { Opt_rsize, "rsize=%u" }, - { Opt_wsize, "wsize=%u" }, - { Opt_bsize, "bsize=%u" }, - { Opt_timeo, "timeo=%u" }, - { Opt_retrans, "retrans=%u" }, - { Opt_acregmin, "acregmin=%u" }, - { Opt_acregmax, "acregmax=%u" }, - { Opt_acdirmin, "acdirmin=%u" }, - { Opt_acdirmax, "acdirmax=%u" }, - { Opt_actimeo, "actimeo=%u" }, - { Opt_namelen, "namlen=%u" }, - { Opt_mountport, "mountport=%u" }, - { Opt_mountvers, "mountvers=%u" }, - { Opt_nfsvers, "nfsvers=%u" }, - { Opt_nfsvers, "vers=%u" }, + { Opt_port, "port=%s" }, + { Opt_rsize, "rsize=%s" }, + { Opt_wsize, "wsize=%s" }, + { Opt_bsize, "bsize=%s" }, + { Opt_timeo, "timeo=%s" }, + { Opt_retrans, "retrans=%s" }, + { Opt_acregmin, "acregmin=%s" }, + { Opt_acregmax, "acregmax=%s" }, + { Opt_acdirmin, "acdirmin=%s" }, + { Opt_acdirmax, "acdirmax=%s" }, + { Opt_actimeo, "actimeo=%s" }, + { Opt_namelen, "namlen=%s" }, + { Opt_mountport, "mountport=%s" }, + { Opt_mountvers, "mountvers=%s" }, + { Opt_nfsvers, "nfsvers=%s" }, + { Opt_nfsvers, "vers=%s" }, { Opt_sec, "sec=%s" }, { Opt_proto, "proto=%s" }, @@ -976,7 +976,8 @@ static int nfs_parse_mount_options(char *raw, while ((p = strsep(&raw, ",")) != NULL) { substring_t args[MAX_OPT_ARGS]; - int option, token; + unsigned long option; + int token; if (!*p) continue; @@ -1085,82 +1086,155 @@ static int nfs_parse_mount_options(char *raw, * options that take numeric values */ case Opt_port: - if (match_int(args, &option) || - option < 0 || option > USHORT_MAX) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0 || option > USHORT_MAX) goto out_invalid_value; mnt->nfs_server.port = option; break; case Opt_rsize: - if (match_int(args, &option) || option < 0) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0) goto out_invalid_value; mnt->rsize = option; break; case Opt_wsize: - if (match_int(args, &option) || option < 0) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0) goto out_invalid_value; mnt->wsize = option; break; case Opt_bsize: - if (match_int(args, &option) || option < 0) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0) goto out_invalid_value; mnt->bsize = option; break; case Opt_timeo: - if (match_int(args, &option) || option <= 0) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0 || option == 0) goto out_invalid_value; mnt->timeo = option; break; case Opt_retrans: - if (match_int(args, &option) || option <= 0) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0 || option == 0) goto out_invalid_value; mnt->retrans = option; break; case Opt_acregmin: - if (match_int(args, &option) || option < 0) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0) goto out_invalid_value; mnt->acregmin = option; break; case Opt_acregmax: - if (match_int(args, &option) || option < 0) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0) goto out_invalid_value; mnt->acregmax = option; break; case Opt_acdirmin: - if (match_int(args, &option) || option < 0) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0) goto out_invalid_value; mnt->acdirmin = option; break; case Opt_acdirmax: - if (match_int(args, &option) || option < 0) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0) goto out_invalid_value; mnt->acdirmax = option; break; case Opt_actimeo: - if (match_int(args, &option) || option < 0) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0) goto out_invalid_value; mnt->acregmin = mnt->acregmax = mnt->acdirmin = mnt->acdirmax = option; break; case Opt_namelen: - if (match_int(args, &option) || option < 0) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0) goto out_invalid_value; mnt->namlen = option; break; case Opt_mountport: - if (match_int(args, &option) || - option < 0 || option > USHORT_MAX) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0 || option > USHORT_MAX) goto out_invalid_value; mnt->mount_server.port = option; break; case Opt_mountvers: - if (match_int(args, &option) || + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0 || option < NFS_MNT_VERSION || option > NFS_MNT3_VERSION) goto out_invalid_value; mnt->mount_server.version = option; break; case Opt_nfsvers: - if (match_int(args, &option)) + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + rc = strict_strtoul(string, 10, &option); + kfree(string); + if (rc != 0) goto out_invalid_value; switch (option) { case NFS2_VERSION: -- cgit v1.2.3 From 4bf259e3ae5015e73282ba66716c4a917e1264ac Mon Sep 17 00:00:00 2001 From: James Morris Date: Wed, 17 Jun 2009 18:02:14 -0700 Subject: nfs: remove unnecessary NFS_INO_INVALID_ACL checks Unless I'm mistaken, NFS_INO_INVALID_ACL is being checked twice during getacl calls (i.e. first via nfs_revalidate_inode() and then by each all site). Signed-off-by: Trond Myklebust --- fs/nfs/nfs3acl.c | 2 -- fs/nfs/nfs4proc.c | 2 -- 2 files changed, 4 deletions(-) diff --git a/fs/nfs/nfs3acl.c b/fs/nfs/nfs3acl.c index 6bbf0e6daad..bac60515a4b 100644 --- a/fs/nfs/nfs3acl.c +++ b/fs/nfs/nfs3acl.c @@ -207,8 +207,6 @@ struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type) status = nfs_revalidate_inode(server, inode); if (status < 0) return ERR_PTR(status); - if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_ACL) - nfs_zap_acl_cache(inode); acl = nfs3_get_cached_acl(inode, type); if (acl != ERR_PTR(-EAGAIN)) return acl; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 55314e72163..42e6a3b0549 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2824,8 +2824,6 @@ static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen) ret = nfs_revalidate_inode(server, inode); if (ret < 0) return ret; - if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_ACL) - nfs_zap_acl_cache(inode); ret = nfs4_read_cached_acl(inode, buf, buflen); if (ret != -ENOENT) return ret; -- cgit v1.2.3 From 578e4585685410cacd1a4ac86b7e3c12805be918 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Thu, 18 Jun 2009 22:01:23 -0400 Subject: nfs41: Move initialization of nfs4_opendata seq_res to nfs4_init_opendata_res nfs4_open_recover_helper clears opendata->o_res before calling nfs4_init_opendata_res, thus causing NFSv4.0 OPEN operations to be sent rather than nfsv4.1. Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 7a750f061c2..92ce4351781 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -680,6 +680,7 @@ static void nfs4_init_opendata_res(struct nfs4_opendata *p) p->o_res.server = p->o_arg.server; nfs_fattr_init(&p->f_attr); nfs_fattr_init(&p->dir_attr); + p->o_res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; } static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path, @@ -711,7 +712,6 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path, p->o_arg.server = server; p->o_arg.bitmask = server->attr_bitmask; p->o_arg.claim = NFS4_OPEN_CLAIM_NULL; - p->o_res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; if (flags & O_EXCL) { u32 *s = (u32 *) p->o_arg.u.verifier.data; s[0] = jiffies; -- cgit v1.2.3 From e9f029855865e917821ef6034b31e340a4cfc815 Mon Sep 17 00:00:00 2001 From: Ricardo Labiaga Date: Thu, 18 Jun 2009 22:01:24 -0400 Subject: nfs41: sunrpc: xprt_alloc_bc_request() should not use spin_lock_bh() xprt_alloc_bc_request() is always called in soft interrupt context. Grab the spin_lock instead of the bottom half spin_lock. Softirqs do not preempt other softirqs running on the same processor, so there is no need to disable bottom halves. Signed-off-by: Ricardo Labiaga Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- net/sunrpc/backchannel_rqst.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/sunrpc/backchannel_rqst.c b/net/sunrpc/backchannel_rqst.c index 5a7d342e308..553621fb2c4 100644 --- a/net/sunrpc/backchannel_rqst.c +++ b/net/sunrpc/backchannel_rqst.c @@ -211,6 +211,9 @@ EXPORT_SYMBOL(xprt_destroy_backchannel); * has been preallocated as well. Use xprt_alloc_bc_request to allocate * to this request. Use xprt_free_bc_request to return it. * + * We know that we're called in soft interrupt context, grab the spin_lock + * since there is no need to grab the bottom half spin_lock. + * * Return an available rpc_rqst, otherwise NULL if non are available. */ struct rpc_rqst *xprt_alloc_bc_request(struct rpc_xprt *xprt) @@ -218,7 +221,7 @@ struct rpc_rqst *xprt_alloc_bc_request(struct rpc_xprt *xprt) struct rpc_rqst *req; dprintk("RPC: allocate a backchannel request\n"); - spin_lock_bh(&xprt->bc_pa_lock); + spin_lock(&xprt->bc_pa_lock); if (!list_empty(&xprt->bc_pa_list)) { req = list_first_entry(&xprt->bc_pa_list, struct rpc_rqst, rq_bc_pa_list); @@ -226,7 +229,7 @@ struct rpc_rqst *xprt_alloc_bc_request(struct rpc_xprt *xprt) } else { req = NULL; } - spin_unlock_bh(&xprt->bc_pa_lock); + spin_unlock(&xprt->bc_pa_lock); if (req != NULL) { set_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state); -- cgit v1.2.3