From 150030b78a454ba50d5e267b0dcf01b162809192 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 6 Dec 2007 16:24:39 -0500 Subject: NFS: Switch from intr mount option to TASK_KILLABLE By using the TASK_KILLABLE infrastructure, we can get rid of the 'intr' mount option. We have to use _killable everywhere instead of _interruptible as we get rid of rpc_clnt_sigmask/sigunmask. Signed-off-by: Liam R. Howlett Signed-off-by: Matthew Wilcox --- fs/nfs/client.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'fs/nfs/client.c') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 70587f383f1..310fa2f4cbb 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -302,7 +302,7 @@ found_client: if (new) nfs_free_client(new); - error = wait_event_interruptible(nfs_client_active_wq, + error = wait_event_killable(nfs_client_active_wq, clp->cl_cons_state != NFS_CS_INITING); if (error < 0) { nfs_put_client(clp); @@ -494,10 +494,6 @@ static int nfs_init_server_rpcclient(struct nfs_server *server, rpc_authflavor_t if (server->flags & NFS_MOUNT_SOFT) server->client->cl_softrtry = 1; - server->client->cl_intr = 0; - if (server->flags & NFS4_MOUNT_INTR) - server->client->cl_intr = 1; - return 0; } -- cgit v1.2.3-70-g09d2 From 5cef338b30c110daf547fb13d99f0c77f2a79fbc Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 11 Dec 2007 22:01:56 -0500 Subject: NFSv2/v3: Fix a memory leak when using -onolock Neil Brown said: > Hi Trond, > > We found that a machine which made moderately heavy use of > 'automount' was leaking some nfs data structures - particularly the > 4K allocated by rpc_alloc_iostats. > It turns out that this only happens with filesystems with -onolock > set. > The problem is that if NFS_MOUNT_NONLM is set, nfs_start_lockd doesn't > set server->destroy, so when the filesystem is unmounted, the > ->client_acl is not shutdown, and so several resources are still > held. Multiple mount/umount cycles will slowly eat away memory > several pages at a time. Signed-off-by: Trond Myklebust Acked-by: NeilBrown --- fs/nfs/client.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'fs/nfs/client.c') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 70587f383f1..a6f62549761 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -410,9 +410,6 @@ static int nfs_create_rpc_client(struct nfs_client *clp, int proto, */ static void nfs_destroy_server(struct nfs_server *server) { - if (!IS_ERR(server->client_acl)) - rpc_shutdown_client(server->client_acl); - if (!(server->flags & NFS_MOUNT_NONLM)) lockd_down(); /* release rpc.lockd */ } @@ -755,6 +752,9 @@ void nfs_free_server(struct nfs_server *server) if (server->destroy != NULL) server->destroy(server); + + if (!IS_ERR(server->client_acl)) + rpc_shutdown_client(server->client_acl); if (!IS_ERR(server->client)) rpc_shutdown_client(server->client); -- cgit v1.2.3-70-g09d2 From ef818a28fac9bd214e676986d8301db0582b92a9 Mon Sep 17 00:00:00 2001 From: Steve Dickson Date: Thu, 8 Nov 2007 04:05:04 -0500 Subject: NFS: Stop sillyname renames and unmounts from racing Added an active/deactive mechanism to the nfs_server structure allowing async operations to hold off umount until the operations are done. Signed-off-by: Steve Dickson Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 3 +++ fs/nfs/internal.h | 2 ++ fs/nfs/super.c | 24 ++++++++++++++++++++++++ fs/nfs/unlink.c | 4 ++++ include/linux/nfs_fs_sb.h | 6 ++++++ 5 files changed, 39 insertions(+) (limited to 'fs/nfs/client.c') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index a6f62549761..c3740f5ab97 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -729,6 +729,9 @@ static struct nfs_server *nfs_alloc_server(void) INIT_LIST_HEAD(&server->client_link); INIT_LIST_HEAD(&server->master_link); + init_waitqueue_head(&server->active_wq); + atomic_set(&server->active, 0); + server->io_stats = nfs_alloc_iostats(); if (!server->io_stats) { kfree(server); diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index f3acf48412b..75793794aef 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -160,6 +160,8 @@ extern struct rpc_stat nfs_rpcstat; extern int __init register_nfs_fs(void); extern void __exit unregister_nfs_fs(void); +extern void nfs_sb_active(struct nfs_server *server); +extern void nfs_sb_deactive(struct nfs_server *server); /* namespace.c */ extern char *nfs_path(const char *base, diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 0b0c72a072f..fda1635dd13 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -202,6 +202,7 @@ static int nfs_get_sb(struct file_system_type *, int, const char *, void *, stru static int nfs_xdev_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); static void nfs_kill_super(struct super_block *); +static void nfs_put_super(struct super_block *); static struct file_system_type nfs_fs_type = { .owner = THIS_MODULE, @@ -223,6 +224,7 @@ static const struct super_operations nfs_sops = { .alloc_inode = nfs_alloc_inode, .destroy_inode = nfs_destroy_inode, .write_inode = nfs_write_inode, + .put_super = nfs_put_super, .statfs = nfs_statfs, .clear_inode = nfs_clear_inode, .umount_begin = nfs_umount_begin, @@ -325,6 +327,28 @@ void __exit unregister_nfs_fs(void) unregister_filesystem(&nfs_fs_type); } +void nfs_sb_active(struct nfs_server *server) +{ + atomic_inc(&server->active); +} + +void nfs_sb_deactive(struct nfs_server *server) +{ + if (atomic_dec_and_test(&server->active)) + wake_up(&server->active_wq); +} + +static void nfs_put_super(struct super_block *sb) +{ + struct nfs_server *server = NFS_SB(sb); + /* + * Make sure there are no outstanding ops to this server. + * If so, wait for them to finish before allowing the + * unmount to continue. + */ + wait_event(server->active_wq, atomic_read(&server->active) == 0); +} + /* * Deliver file system statistics to userspace */ diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index 431981d0265..8e5428e0b86 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c @@ -14,6 +14,8 @@ #include #include +#include "internal.h" + struct nfs_unlinkdata { struct hlist_node list; struct nfs_removeargs args; @@ -113,6 +115,7 @@ static void nfs_async_unlink_release(void *calldata) struct nfs_unlinkdata *data = calldata; nfs_dec_sillycount(data->dir); + nfs_sb_deactive(NFS_SERVER(data->dir)); nfs_free_unlinkdata(data); } @@ -153,6 +156,7 @@ static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct n nfs_dec_sillycount(dir); return 0; } + nfs_sb_active(NFS_SERVER(dir)); data->args.fh = NFS_FH(dir); nfs_fattr_init(&data->res.dir_attr); diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 0cac49bc095..9f949b58768 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -3,6 +3,9 @@ #include #include +#include + +#include struct nfs_iostats; @@ -110,6 +113,9 @@ struct nfs_server { filesystem */ #endif void (*destroy)(struct nfs_server *); + + atomic_t active; /* Keep trace of any activity to this server */ + wait_queue_head_t active_wq; /* Wait for any activity to stop */ }; /* Server capabilities */ -- cgit v1.2.3-70-g09d2 From d45273ed6f4613e81701c3e896d9db200c288fff Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 26 Oct 2007 13:32:45 -0400 Subject: NFS: Clean up address comparison in __nfs_find_client() The address comparison in the __nfs_find_client() function is deceptive. It uses a memcmp() to check a pair of u32 fields for equality. Not only is this inefficient, but usually memcmp() is used for comparing two *whole* sockaddr_in's (which includes comparisons of the address family and port number), so it's easy to mistake the comparison here for a whole sockaddr comparison, which it isn't. So for clarity and efficiency, we replace the memcmp() with a simple test for equality between the two s_addr fields. This should have no behavioral effect. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'fs/nfs/client.c') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index c3740f5ab97..8b5f9b9685d 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -220,8 +220,7 @@ static struct nfs_client *__nfs_find_client(const struct sockaddr_in *addr, int if (clp->cl_nfsversion != nfsversion) continue; - if (memcmp(&clp->cl_addr.sin_addr, &addr->sin_addr, - sizeof(clp->cl_addr.sin_addr)) != 0) + if (clp->cl_addr.sin_addr.s_addr != addr->sin_addr.s_addr) continue; if (!match_port || clp->cl_addr.sin_port == addr->sin_port) -- cgit v1.2.3-70-g09d2 From 3a498026eef9603c14037e73a4a94cfdb2fa44eb Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 14 Dec 2007 14:56:04 -0500 Subject: NFS: Clean up the nfs_client initialisation Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 51 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 20 deletions(-) (limited to 'fs/nfs/client.c') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 8b5f9b9685d..d7f6d50442b 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -93,22 +93,26 @@ struct rpc_program nfsacl_program = { }; #endif /* CONFIG_NFS_V3_ACL */ +struct nfs_client_initdata { + const char *hostname; + const struct sockaddr_in *addr; + int version; +}; + /* * Allocate a shared client record * * Since these are allocated/deallocated very rarely, we don't * bother putting them in a slab cache... */ -static struct nfs_client *nfs_alloc_client(const char *hostname, - const struct sockaddr_in *addr, - int nfsversion) +static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) { struct nfs_client *clp; if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) goto error_0; - if (nfsversion == 4) { + if (cl_init->version == 4) { if (nfs_callback_up() < 0) goto error_2; __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state); @@ -117,11 +121,11 @@ static struct nfs_client *nfs_alloc_client(const char *hostname, atomic_set(&clp->cl_count, 1); clp->cl_cons_state = NFS_CS_INITING; - clp->cl_nfsversion = nfsversion; - memcpy(&clp->cl_addr, addr, sizeof(clp->cl_addr)); + clp->cl_nfsversion = cl_init->version; + memcpy(&clp->cl_addr, cl_init->addr, sizeof(clp->cl_addr)); - if (hostname) { - clp->cl_hostname = kstrdup(hostname, GFP_KERNEL); + if (cl_init->hostname) { + clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL); if (!clp->cl_hostname) goto error_3; } @@ -256,22 +260,20 @@ struct nfs_client *nfs_find_client(const struct sockaddr_in *addr, int nfsversio * Look up a client by IP address and protocol version * - creates a new record if one doesn't yet exist */ -static struct nfs_client *nfs_get_client(const char *hostname, - const struct sockaddr_in *addr, - int nfsversion) +static struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_init) { struct nfs_client *clp, *new = NULL; int error; dprintk("--> nfs_get_client(%s,"NIPQUAD_FMT":%d,%d)\n", - hostname ?: "", NIPQUAD(addr->sin_addr), - addr->sin_port, nfsversion); + cl_init->hostname ?: "", NIPQUAD(cl_init->addr->sin_addr), + cl_init->addr->sin_port, cl_init->version); /* see if the client already exists */ do { spin_lock(&nfs_client_lock); - clp = __nfs_find_client(addr, nfsversion, 1); + clp = __nfs_find_client(cl_init->addr, cl_init->version, 1); if (clp) goto found_client; if (new) @@ -279,7 +281,7 @@ static struct nfs_client *nfs_get_client(const char *hostname, spin_unlock(&nfs_client_lock); - new = nfs_alloc_client(hostname, addr, nfsversion); + new = nfs_alloc_client(cl_init); } while (new); return ERR_PTR(-ENOMEM); @@ -540,19 +542,23 @@ error: static int nfs_init_server(struct nfs_server *server, const struct nfs_parsed_mount_data *data) { + struct nfs_client_initdata cl_init = { + .hostname = data->nfs_server.hostname, + .addr = &data->nfs_server.address, + .version = 2, + }; struct nfs_client *clp; - int error, nfsvers = 2; + int error; dprintk("--> nfs_init_server()\n"); #ifdef CONFIG_NFS_V3 if (data->flags & NFS_MOUNT_VER3) - nfsvers = 3; + cl_init.version = 3; #endif /* Allocate or find a client reference we can use */ - clp = nfs_get_client(data->nfs_server.hostname, - &data->nfs_server.address, nfsvers); + clp = nfs_get_client(&cl_init); if (IS_ERR(clp)) { dprintk("<-- nfs_init_server() = error %ld\n", PTR_ERR(clp)); return PTR_ERR(clp); @@ -889,13 +895,18 @@ static int nfs4_set_client(struct nfs_server *server, rpc_authflavor_t authflavour, int proto, int timeo, int retrans) { + struct nfs_client_initdata cl_init = { + .hostname = hostname, + .addr = addr, + .version = 4, + }; struct nfs_client *clp; int error; dprintk("--> nfs4_set_client()\n"); /* Allocate or find a client reference we can use */ - clp = nfs_get_client(hostname, addr, 4); + clp = nfs_get_client(&cl_init); if (IS_ERR(clp)) { error = PTR_ERR(clp); goto error; -- cgit v1.2.3-70-g09d2 From c81468a1a766921f11ae44e8a99816ac8dc7b015 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 14 Dec 2007 14:56:05 -0500 Subject: NFS: Clean up the nfs_find_client function. Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 52 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 22 deletions(-) (limited to 'fs/nfs/client.c') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index d7f6d50442b..ff778ecee0b 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -208,52 +208,60 @@ void nfs_put_client(struct nfs_client *clp) } /* - * Find a client by address - * - caller must hold nfs_client_lock + * Find a client by IP address and protocol version + * - returns NULL if no such client */ -static struct nfs_client *__nfs_find_client(const struct sockaddr_in *addr, int nfsversion, int match_port) +struct nfs_client *nfs_find_client(const struct sockaddr_in *addr, int nfsversion) { struct nfs_client *clp; + spin_lock(&nfs_client_lock); list_for_each_entry(clp, &nfs_client_list, cl_share_link) { /* Don't match clients that failed to initialise properly */ - if (clp->cl_cons_state < 0) + if (clp->cl_cons_state != NFS_CS_READY) continue; /* Different NFS versions cannot share the same nfs_client */ if (clp->cl_nfsversion != nfsversion) continue; + /* Match only the IP address, not the port number */ if (clp->cl_addr.sin_addr.s_addr != addr->sin_addr.s_addr) continue; - if (!match_port || clp->cl_addr.sin_port == addr->sin_port) - goto found; + atomic_inc(&clp->cl_count); + spin_unlock(&nfs_client_lock); + return clp; } - + spin_unlock(&nfs_client_lock); return NULL; - -found: - atomic_inc(&clp->cl_count); - return clp; } /* - * Find a client by IP address and protocol version - * - returns NULL if no such client + * Find an nfs_client on the list that matches the initialisation data + * that is supplied. */ -struct nfs_client *nfs_find_client(const struct sockaddr_in *addr, int nfsversion) +static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *data) { struct nfs_client *clp; - spin_lock(&nfs_client_lock); - clp = __nfs_find_client(addr, nfsversion, 0); - spin_unlock(&nfs_client_lock); - if (clp != NULL && clp->cl_cons_state != NFS_CS_READY) { - nfs_put_client(clp); - clp = NULL; + list_for_each_entry(clp, &nfs_client_list, cl_share_link) { + /* Don't match clients that failed to initialise properly */ + if (clp->cl_cons_state < 0) + continue; + + /* Different NFS versions cannot share the same nfs_client */ + if (clp->cl_nfsversion != data->version) + continue; + + /* Match the full socket address */ + if (memcmp(&clp->cl_addr, data->addr, sizeof(clp->cl_addr)) != 0) + continue; + + atomic_inc(&clp->cl_count); + return clp; } - return clp; + return NULL; } /* @@ -273,7 +281,7 @@ static struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_in do { spin_lock(&nfs_client_lock); - clp = __nfs_find_client(cl_init->addr, cl_init->version, 1); + clp = nfs_match_client(cl_init); if (clp) goto found_client; if (new) -- cgit v1.2.3-70-g09d2 From 40c553193df41920de659f0446e5d214c862e827 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 14 Dec 2007 14:56:07 -0500 Subject: NFS: Remove the redundant nfs_client->cl_nfsversion We can get the same information from the rpc_ops structure instead. Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 41 ++++++++++++++++++----------------------- fs/nfs/namespace.c | 2 +- fs/nfs/super.c | 2 +- include/linux/nfs_fs_sb.h | 1 - 4 files changed, 20 insertions(+), 26 deletions(-) (limited to 'fs/nfs/client.c') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index ff778ecee0b..3b21731ae57 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -96,7 +96,7 @@ struct rpc_program nfsacl_program = { struct nfs_client_initdata { const char *hostname; const struct sockaddr_in *addr; - int version; + const struct nfs_rpc_ops *rpc_ops; }; /* @@ -112,7 +112,9 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_ if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) goto error_0; - if (cl_init->version == 4) { + 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); @@ -121,7 +123,6 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_ atomic_set(&clp->cl_count, 1); clp->cl_cons_state = NFS_CS_INITING; - clp->cl_nfsversion = cl_init->version; memcpy(&clp->cl_addr, cl_init->addr, sizeof(clp->cl_addr)); if (cl_init->hostname) { @@ -170,7 +171,7 @@ static void nfs4_shutdown_client(struct nfs_client *clp) */ static void nfs_free_client(struct nfs_client *clp) { - dprintk("--> nfs_free_client(%d)\n", clp->cl_nfsversion); + dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version); nfs4_shutdown_client(clp); @@ -222,7 +223,7 @@ struct nfs_client *nfs_find_client(const struct sockaddr_in *addr, int nfsversio continue; /* Different NFS versions cannot share the same nfs_client */ - if (clp->cl_nfsversion != nfsversion) + if (clp->rpc_ops->version != nfsversion) continue; /* Match only the IP address, not the port number */ @@ -251,7 +252,7 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat continue; /* Different NFS versions cannot share the same nfs_client */ - if (clp->cl_nfsversion != data->version) + if (clp->rpc_ops != data->rpc_ops) continue; /* Match the full socket address */ @@ -273,9 +274,9 @@ static struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_in struct nfs_client *clp, *new = NULL; int error; - dprintk("--> nfs_get_client(%s,"NIPQUAD_FMT":%d,%d)\n", + dprintk("--> nfs_get_client(%s,"NIPQUAD_FMT":%d,%u)\n", cl_init->hostname ?: "", NIPQUAD(cl_init->addr->sin_addr), - cl_init->addr->sin_port, cl_init->version); + cl_init->addr->sin_port, cl_init->rpc_ops->version); /* see if the client already exists */ do { @@ -430,7 +431,7 @@ static int nfs_start_lockd(struct nfs_server *server) { int error = 0; - if (server->nfs_client->cl_nfsversion > 3) + if (server->nfs_client->rpc_ops->version > 3) goto out; if (server->flags & NFS_MOUNT_NONLM) goto out; @@ -450,7 +451,7 @@ out: #ifdef CONFIG_NFS_V3_ACL static void nfs_init_server_aclclient(struct nfs_server *server) { - if (server->nfs_client->cl_nfsversion != 3) + if (server->nfs_client->rpc_ops->version != 3) goto out_noacl; if (server->flags & NFS_MOUNT_NOACL) goto out_noacl; @@ -521,12 +522,6 @@ static int nfs_init_client(struct nfs_client *clp, return 0; } - /* Check NFS protocol revision and initialize RPC op vector */ - clp->rpc_ops = &nfs_v2_clientops; -#ifdef CONFIG_NFS_V3 - if (clp->cl_nfsversion == 3) - clp->rpc_ops = &nfs_v3_clientops; -#endif /* * Create a client RPC handle for doing FSSTAT with UNIX auth only * - RFC 2623, sec 2.3.2 @@ -553,7 +548,7 @@ static int nfs_init_server(struct nfs_server *server, struct nfs_client_initdata cl_init = { .hostname = data->nfs_server.hostname, .addr = &data->nfs_server.address, - .version = 2, + .rpc_ops = &nfs_v2_clientops, }; struct nfs_client *clp; int error; @@ -562,7 +557,7 @@ static int nfs_init_server(struct nfs_server *server, #ifdef CONFIG_NFS_V3 if (data->flags & NFS_MOUNT_VER3) - cl_init.version = 3; + cl_init.rpc_ops = &nfs_v3_clientops; #endif /* Allocate or find a client reference we can use */ @@ -906,7 +901,7 @@ static int nfs4_set_client(struct nfs_server *server, struct nfs_client_initdata cl_init = { .hostname = hostname, .addr = addr, - .version = 4, + .rpc_ops = &nfs_v4_clientops, }; struct nfs_client *clp; int error; @@ -1284,8 +1279,8 @@ static int nfs_server_list_show(struct seq_file *m, void *v) /* display one transport per line on subsequent lines */ clp = list_entry(v, struct nfs_client, cl_share_link); - seq_printf(m, "v%d %02x%02x%02x%02x %4hx %3d %s\n", - clp->cl_nfsversion, + seq_printf(m, "v%u %02x%02x%02x%02x %4hx %3d %s\n", + clp->rpc_ops->version, NIPQUAD(clp->cl_addr.sin_addr), ntohs(clp->cl_addr.sin_port), atomic_read(&clp->cl_count), @@ -1363,8 +1358,8 @@ static int nfs_volume_list_show(struct seq_file *m, void *v) (unsigned long long) server->fsid.major, (unsigned long long) server->fsid.minor); - seq_printf(m, "v%d %02x%02x%02x%02x %4hx %-7s %-17s\n", - clp->cl_nfsversion, + seq_printf(m, "v%u %02x%02x%02x%02x %4hx %-7s %-17s\n", + clp->rpc_ops->version, NIPQUAD(clp->cl_addr.sin_addr), ntohs(clp->cl_addr.sin_port), dev, diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c index acfc56f9edc..be4ce1c3a3d 100644 --- a/fs/nfs/namespace.c +++ b/fs/nfs/namespace.c @@ -188,7 +188,7 @@ static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server, { #ifdef CONFIG_NFS_V4 struct vfsmount *mnt = NULL; - switch (server->nfs_client->cl_nfsversion) { + switch (server->nfs_client->rpc_ops->version) { case 2: case 3: mnt = vfs_kern_mount(&nfs_xdev_fs_type, 0, devname, mountdata); diff --git a/fs/nfs/super.c b/fs/nfs/super.c index a3492d6f8f9..5608e6a4c1e 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -529,7 +529,7 @@ static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt) seq_printf(m, ",namelen=%d", nfss->namelen); #ifdef CONFIG_NFS_V4 - if (nfss->nfs_client->cl_nfsversion == 4) { + if (nfss->nfs_client->rpc_ops->version == 4) { seq_printf(m, "\n\tnfsv4:\t"); seq_printf(m, "bm0=0x%x", nfss->attr_bitmask[0]); seq_printf(m, ",bm1=0x%x", nfss->attr_bitmask[1]); diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 9f949b58768..97b32575666 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -17,7 +17,6 @@ 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 */ - int cl_nfsversion; /* NFS protocol version */ 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-70-g09d2 From 5d8515caeb99940f5ed56d22a03aba20bbe7fdcb Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 10 Dec 2007 14:57:16 -0500 Subject: NFS: eliminate NIPQUAD(clp->cl_addr.sin_addr) To ensure the NFS client displays IPv6 addresses properly, replace address family-specific NIPQUAD() invocations with a call to the RPC client to get a formatted string representing the remote peer's address. Signed-off-by: Chuck Lever Cc: Aurelien Charbon Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 12 ++++++------ fs/nfs/delegation.c | 10 ++++++---- fs/nfs/nfs4state.c | 9 +++++---- fs/nfs/super.c | 5 +++-- 4 files changed, 20 insertions(+), 16 deletions(-) (limited to 'fs/nfs/client.c') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 3b21731ae57..701cd193a01 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1279,10 +1279,10 @@ static int nfs_server_list_show(struct seq_file *m, void *v) /* display one transport per line on subsequent lines */ clp = list_entry(v, struct nfs_client, cl_share_link); - seq_printf(m, "v%u %02x%02x%02x%02x %4hx %3d %s\n", + seq_printf(m, "v%u %s %s %3d %s\n", clp->rpc_ops->version, - NIPQUAD(clp->cl_addr.sin_addr), - ntohs(clp->cl_addr.sin_port), + rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), + rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), atomic_read(&clp->cl_count), clp->cl_hostname); @@ -1358,10 +1358,10 @@ static int nfs_volume_list_show(struct seq_file *m, void *v) (unsigned long long) server->fsid.major, (unsigned long long) server->fsid.minor); - seq_printf(m, "v%u %02x%02x%02x%02x %4hx %-7s %-17s\n", + seq_printf(m, "v%u %s %s %-7s %-17s\n", clp->rpc_ops->version, - NIPQUAD(clp->cl_addr.sin_addr), - ntohs(clp->cl_addr.sin_port), + rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), + rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), dev, fsid); diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 11833f4caea..b03dcd8403f 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -156,8 +156,9 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct if (memcmp(&delegation->stateid, &nfsi->delegation->stateid, sizeof(delegation->stateid)) != 0 || delegation->type != nfsi->delegation->type) { - printk("%s: server %u.%u.%u.%u, handed out a duplicate delegation!\n", - __FUNCTION__, NIPQUAD(clp->cl_addr.sin_addr)); + printk(KERN_WARNING "%s: server %s handed out " + "a duplicate delegation!\n", + __FUNCTION__, clp->cl_hostname); status = -EIO; } } @@ -314,8 +315,9 @@ void nfs_expire_all_delegations(struct nfs_client *clp) __module_get(THIS_MODULE); atomic_inc(&clp->cl_count); task = kthread_run(nfs_do_expire_all_delegations, clp, - "%u.%u.%u.%u-delegreturn", - NIPQUAD(clp->cl_addr.sin_addr)); + "%s-delegreturn", + rpc_peeraddr2str(clp->cl_rpcclient, + RPC_DISPLAY_ADDR)); if (!IS_ERR(task)) return; nfs_put_client(clp); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index bf94c6e0a50..f9c7432471d 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -758,8 +758,9 @@ static void nfs4_recover_state(struct nfs_client *clp) __module_get(THIS_MODULE); atomic_inc(&clp->cl_count); - task = kthread_run(reclaimer, clp, "%u.%u.%u.%u-reclaim", - NIPQUAD(clp->cl_addr.sin_addr)); + task = kthread_run(reclaimer, clp, "%s-reclaim", + rpc_peeraddr2str(clp->cl_rpcclient, + RPC_DISPLAY_ADDR)); if (!IS_ERR(task)) return; nfs4_clear_recover_bit(clp); @@ -970,8 +971,8 @@ out: module_put_and_exit(0); return 0; out_error: - printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %u.%u.%u.%u with error %d\n", - NIPQUAD(clp->cl_addr.sin_addr), -status); + printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %s" + " with error %d\n", clp->cl_hostname, -status); set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); goto out; } diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 5608e6a4c1e..75f3cbf922a 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -491,8 +491,9 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt) nfs_show_mount_options(m, nfss, 0); - seq_printf(m, ",addr="NIPQUAD_FMT, - NIPQUAD(nfss->nfs_client->cl_addr.sin_addr)); + seq_printf(m, ",addr=%s", + rpc_peeraddr2str(nfss->nfs_client->cl_rpcclient, + RPC_DISPLAY_ADDR)); return 0; } -- cgit v1.2.3-70-g09d2 From 3b0d3f93d01bb013c3dcf9555d2d111c91ac6a1e Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 3 Jan 2008 13:28:58 -0500 Subject: NFS: Add support for AF_INET6 addresses in __nfs_find_client() Introduce AF_INET6-specific address checking to __nfs_find_client(). Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) (limited to 'fs/nfs/client.c') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 701cd193a01..876162cddf1 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include #include @@ -208,16 +210,44 @@ void nfs_put_client(struct nfs_client *clp) } } +static int nfs_sockaddr_match_ipaddr4(const struct sockaddr_in *sa1, + const struct sockaddr_in *sa2) +{ + return sa1->sin_addr.s_addr == sa2->sin_addr.s_addr; +} + +static int nfs_sockaddr_match_ipaddr6(const struct sockaddr_in6 *sa1, + const struct sockaddr_in6 *sa2) +{ + return ipv6_addr_equal(&sa1->sin6_addr, &sa2->sin6_addr); +} + +static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1, + const struct sockaddr *sa2) +{ + switch (sa1->sa_family) { + case AF_INET: + return nfs_sockaddr_match_ipaddr4((const struct sockaddr_in *)sa1, + (const struct sockaddr_in *)sa2); + case AF_INET6: + return nfs_sockaddr_match_ipaddr6((const struct sockaddr_in6 *)sa1, + (const struct sockaddr_in6 *)sa2); + } + BUG(); +} + /* * Find a client by IP address and protocol version * - returns NULL if no such client */ -struct nfs_client *nfs_find_client(const struct sockaddr_in *addr, int nfsversion) +struct nfs_client *_nfs_find_client(const struct sockaddr *addr, int nfsversion) { struct nfs_client *clp; spin_lock(&nfs_client_lock); list_for_each_entry(clp, &nfs_client_list, cl_share_link) { + 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) continue; @@ -226,8 +256,10 @@ struct nfs_client *nfs_find_client(const struct sockaddr_in *addr, int nfsversio if (clp->rpc_ops->version != nfsversion) continue; + if (addr->sa_family != clap->sa_family) + continue; /* Match only the IP address, not the port number */ - if (clp->cl_addr.sin_addr.s_addr != addr->sin_addr.s_addr) + if (!nfs_sockaddr_match_ipaddr(addr, clap)) continue; atomic_inc(&clp->cl_count); @@ -238,6 +270,11 @@ struct nfs_client *nfs_find_client(const struct sockaddr_in *addr, int nfsversio return NULL; } +struct nfs_client *nfs_find_client(const struct sockaddr_in *addr, int nfsversion) +{ + return _nfs_find_client((const struct sockaddr *)addr, nfsversion); +} + /* * Find an nfs_client on the list that matches the initialisation data * that is supplied. -- cgit v1.2.3-70-g09d2 From 6e4cffd7b2cf86022dcf9cceeb63f16ff852caa1 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 10 Dec 2007 14:58:15 -0500 Subject: NFS: Expand server address storage in nfs_client struct Prepare for managing larger addresses in the NFS client by widening the nfs_client struct's cl_addr field. Signed-off-by: Chuck Lever Cc: Aurelien Charbon (Modified to work with the new parameters for nfs_alloc_client) Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 8 ++++++-- include/linux/nfs_fs_sb.h | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'fs/nfs/client.c') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 876162cddf1..44fe7fd7bfb 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -98,6 +98,7 @@ struct rpc_program nfsacl_program = { struct nfs_client_initdata { const char *hostname; const struct sockaddr_in *addr; + size_t addrlen; const struct nfs_rpc_ops *rpc_ops; }; @@ -125,7 +126,8 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_ atomic_set(&clp->cl_count, 1); clp->cl_cons_state = NFS_CS_INITING; - memcpy(&clp->cl_addr, cl_init->addr, sizeof(clp->cl_addr)); + memcpy(&clp->cl_addr, cl_init->addr, cl_init->addrlen); + clp->cl_addrlen = cl_init->addrlen; if (cl_init->hostname) { clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL); @@ -425,7 +427,7 @@ static int nfs_create_rpc_client(struct nfs_client *clp, int proto, struct rpc_create_args args = { .protocol = proto, .address = (struct sockaddr *)&clp->cl_addr, - .addrsize = sizeof(clp->cl_addr), + .addrsize = clp->cl_addrlen, .timeout = &timeparms, .servername = clp->cl_hostname, .program = &nfs_program, @@ -585,6 +587,7 @@ static int nfs_init_server(struct nfs_server *server, struct nfs_client_initdata cl_init = { .hostname = data->nfs_server.hostname, .addr = &data->nfs_server.address, + .addrlen = sizeof(data->nfs_server.address), .rpc_ops = &nfs_v2_clientops, }; struct nfs_client *clp; @@ -938,6 +941,7 @@ static int nfs4_set_client(struct nfs_server *server, struct nfs_client_initdata cl_init = { .hostname = hostname, .addr = addr, + .addrlen = sizeof(*addr), .rpc_ops = &nfs_v4_clientops, }; struct nfs_client *clp; diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 0b3dcba3d97..912cfe84ea8 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -21,7 +21,8 @@ struct nfs_client { #define NFS_CS_CALLBACK 1 /* - callback started */ #define NFS_CS_IDMAP 2 /* - idmap started */ #define NFS_CS_RENEWD 3 /* - renewd started */ - struct sockaddr_in cl_addr; /* server identifier */ + struct sockaddr_storage cl_addr; /* server identifier */ + size_t cl_addrlen; char * cl_hostname; /* hostname of server */ struct list_head cl_share_link; /* link in global client list */ struct list_head cl_superblocks; /* List of nfs_server structs */ -- cgit v1.2.3-70-g09d2 From ff052645c939b2fd8d467105adf98fa621cc244b Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 10 Dec 2007 14:58:44 -0500 Subject: NFS: Change nfs_find_client() to take "struct sockaddr *" Adjust arguments and callers of nfs_find_client() to pass a "struct sockaddr *" instead of "struct sockaddr_in *" to support non-IPv4 addresses. Signed-off-by: Chuck Lever Cc: Aurelien Charbon Trond: Also fix up protocol version number argument in nfs_find_client() to use the correct u32 type. Signed-off-by: Trond Myklebust --- fs/nfs/callback.c | 3 +-- fs/nfs/callback_proc.c | 4 ++-- fs/nfs/client.c | 7 +------ fs/nfs/internal.h | 2 +- 4 files changed, 5 insertions(+), 11 deletions(-) (limited to 'fs/nfs/client.c') diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index bbf67f148ff..9b6bbf1b978 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -164,12 +164,11 @@ void nfs_callback_down(void) static int nfs_callback_authenticate(struct svc_rqst *rqstp) { - struct sockaddr_in *addr = svc_addr_in(rqstp); struct nfs_client *clp; char buf[RPC_MAX_ADDRBUFLEN]; /* Don't talk to strangers */ - clp = nfs_find_client(addr, 4); + clp = nfs_find_client(svc_addr(rqstp), 4); if (clp == NULL) return SVC_DROP; diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index fa9586dcc3d..e89a9007c91 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -25,7 +25,7 @@ __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres * res->bitmap[0] = res->bitmap[1] = 0; res->status = htonl(NFS4ERR_BADHANDLE); - clp = nfs_find_client((struct sockaddr_in *)args->addr, 4); + clp = nfs_find_client(args->addr, 4); if (clp == NULL) goto out; @@ -68,7 +68,7 @@ __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy) __be32 res; res = htonl(NFS4ERR_BADHANDLE); - clp = nfs_find_client((struct sockaddr_in *)args->addr, 4); + clp = nfs_find_client(args->addr, 4); if (clp == NULL) goto out; diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 44fe7fd7bfb..73bf4ecad03 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -242,7 +242,7 @@ static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1, * Find a client by IP address and protocol version * - returns NULL if no such client */ -struct nfs_client *_nfs_find_client(const struct sockaddr *addr, int nfsversion) +struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion) { struct nfs_client *clp; @@ -272,11 +272,6 @@ struct nfs_client *_nfs_find_client(const struct sockaddr *addr, int nfsversion) return NULL; } -struct nfs_client *nfs_find_client(const struct sockaddr_in *addr, int nfsversion) -{ - return _nfs_find_client((const struct sockaddr *)addr, nfsversion); -} - /* * Find an nfs_client on the list that matches the initialisation data * that is supplied. diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 058d503a0ee..c8458b16801 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -60,7 +60,7 @@ struct nfs_parsed_mount_data { extern struct rpc_program nfs_program; extern void nfs_put_client(struct nfs_client *); -extern struct nfs_client *nfs_find_client(const struct sockaddr_in *, int); +extern struct nfs_client *nfs_find_client(const struct sockaddr *, u32); extern struct nfs_server *nfs_create_server( const struct nfs_parsed_mount_data *, struct nfs_fh *); -- cgit v1.2.3-70-g09d2 From d7422c472bbaa419876b91e8823c6219c4a144cb Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 10 Dec 2007 14:58:51 -0500 Subject: NFS: Change nfs_get_client() to take sockaddr * Adjust arguments and callers of nfs_get_client() to pass a "struct sockaddr *" instead of "struct sockaddr_in *" to support non-IPv4 addresses. Signed-off-by: Chuck Lever Cc: Aurelien Charbon Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'fs/nfs/client.c') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 73bf4ecad03..e43072bdbb0 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -97,7 +97,7 @@ struct rpc_program nfsacl_program = { struct nfs_client_initdata { const char *hostname; - const struct sockaddr_in *addr; + const struct sockaddr *addr; size_t addrlen; const struct nfs_rpc_ops *rpc_ops; }; @@ -308,9 +308,8 @@ static struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_in struct nfs_client *clp, *new = NULL; int error; - dprintk("--> nfs_get_client(%s,"NIPQUAD_FMT":%d,%u)\n", - cl_init->hostname ?: "", NIPQUAD(cl_init->addr->sin_addr), - cl_init->addr->sin_port, cl_init->rpc_ops->version); + dprintk("--> nfs_get_client(%s,v%u)\n", + cl_init->hostname ?: "", cl_init->rpc_ops->version); /* see if the client already exists */ do { @@ -581,7 +580,7 @@ static int nfs_init_server(struct nfs_server *server, { struct nfs_client_initdata cl_init = { .hostname = data->nfs_server.hostname, - .addr = &data->nfs_server.address, + .addr = (const struct sockaddr *)&data->nfs_server.address, .addrlen = sizeof(data->nfs_server.address), .rpc_ops = &nfs_v2_clientops, }; @@ -935,7 +934,7 @@ static int nfs4_set_client(struct nfs_server *server, { struct nfs_client_initdata cl_init = { .hostname = hostname, - .addr = addr, + .addr = (const struct sockaddr *)addr, .addrlen = sizeof(*addr), .rpc_ops = &nfs_v4_clientops, }; -- cgit v1.2.3-70-g09d2 From dcecae0ff44dceea7adb6bef5c8eb660fe87a93c Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 10 Dec 2007 14:58:59 -0500 Subject: NFS: Change nfs4_set_client() to accept struct sockaddr * Adjust the arguments and callers of nfs4_set_client() to pass a "struct sockaddr *" instead of a "struct sockaddr_in *" to support non-IPv4 addresses in the NFS client. Signed-off-by: Chuck Lever Cc: Aurelien Charbon Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'fs/nfs/client.c') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index e43072bdbb0..11380601fc7 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -927,15 +927,17 @@ error: * Set up an NFS4 client */ static int nfs4_set_client(struct nfs_server *server, - const char *hostname, const struct sockaddr_in *addr, + const char *hostname, + const struct sockaddr *addr, + const size_t addrlen, const char *ip_addr, rpc_authflavor_t authflavour, int proto, int timeo, int retrans) { struct nfs_client_initdata cl_init = { .hostname = hostname, - .addr = (const struct sockaddr *)addr, - .addrlen = sizeof(*addr), + .addr = addr, + .addrlen = addrlen, .rpc_ops = &nfs_v4_clientops, }; struct nfs_client *clp; @@ -1015,7 +1017,8 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, /* Get a client record */ error = nfs4_set_client(server, data->nfs_server.hostname, - &data->nfs_server.address, + (struct sockaddr *)&data->nfs_server.address, + sizeof(data->nfs_server.address), data->client_address, data->auth_flavors[0], data->nfs_server.protocol, @@ -1090,12 +1093,14 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, /* Get a client representation. * Note: NFSv4 always uses TCP, */ - error = nfs4_set_client(server, data->hostname, data->addr, - parent_client->cl_ipaddr, - data->authflavor, - parent_server->client->cl_xprt->prot, - parent_client->retrans_timeo, - parent_client->retrans_count); + error = nfs4_set_client(server, data->hostname, + (struct sockaddr *)data->addr, + sizeof(*data->addr), + parent_client->cl_ipaddr, + data->authflavor, + parent_server->client->cl_xprt->prot, + parent_client->retrans_timeo, + parent_client->retrans_count); if (error < 0) goto error; -- cgit v1.2.3-70-g09d2 From 6677d09513e35ac2f38d3a8c8a26fbd7bbcef192 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 10 Dec 2007 14:59:06 -0500 Subject: NFS: Adjust nfs_clone_mount structure to store "struct sockaddr *" Change the addr field in the nfs_clone_mount structure to store a "struct sockaddr *" to support non-IPv4 addresses in the NFS client. Note this is mostly a cosmetic change, and does not actually allow referrals using IPv6 addresses. The existing referral code assumes that the server returns a string that represents an IPv4 address. This code needs to support hostnames and IPv6 addresses as well as IPv4 addresses, thus it will need to be reorganized completely (to handle DNS resolution in user space). Signed-off-by: Chuck Lever Cc: Aurelien Charbon Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 4 ++-- fs/nfs/internal.h | 3 ++- fs/nfs/nfs4namespace.c | 12 +++++++----- 3 files changed, 11 insertions(+), 8 deletions(-) (limited to 'fs/nfs/client.c') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 11380601fc7..ba114faf195 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1094,8 +1094,8 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, /* Get a client representation. * Note: NFSv4 always uses TCP, */ error = nfs4_set_client(server, data->hostname, - (struct sockaddr *)data->addr, - sizeof(*data->addr), + data->addr, + data->addrlen, parent_client->cl_ipaddr, data->authflavor, parent_server->client->cl_xprt->prot, diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index c8458b16801..75dd4e252ca 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -21,7 +21,8 @@ struct nfs_clone_mount { struct nfs_fattr *fattr; char *hostname; char *mnt_path; - struct sockaddr_in *addr; + struct sockaddr *addr; + size_t addrlen; rpc_authflavor_t authflavor; }; diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c index bd1b1617905..5f9ba41ed5b 100644 --- a/fs/nfs/nfs4namespace.c +++ b/fs/nfs/nfs4namespace.c @@ -172,7 +172,10 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent, s = 0; while (s < location->nservers) { - struct sockaddr_in addr = {}; + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = htons(NFS_PORT), + }; if (location->servers[s].len <= 0 || valid_ipaddr4(location->servers[s].data) < 0) { @@ -181,10 +184,9 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent, } mountdata.hostname = location->servers[s].data; - addr.sin_addr.s_addr = in_aton(mountdata.hostname); - addr.sin_family = AF_INET; - addr.sin_port = htons(NFS_PORT); - mountdata.addr = &addr; + addr.sin_addr.s_addr = in_aton(mountdata.hostname), + mountdata.addr = (struct sockaddr *)&addr; + mountdata.addrlen = sizeof(addr); snprintf(page, PAGE_SIZE, "%s:%s", mountdata.hostname, -- cgit v1.2.3-70-g09d2 From 4c5680177012a2b5c0f3fdf58f4375dd84a1da67 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 10 Dec 2007 14:59:28 -0500 Subject: NFS: Support non-IPv4 addresses in nfs_parsed_mount_data Replace the nfs_server and mount_server address fields in the nfs_parsed_mount_data structure with a "struct sockaddr_storage" instead of a "struct sockaddr_in". Signed-off-by: Chuck Lever Cc: Aurelien Charbon Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 4 ++-- fs/nfs/internal.h | 6 ++++-- fs/nfs/super.c | 54 ++++++++++++++++++++++++++++++++++-------------------- 3 files changed, 40 insertions(+), 24 deletions(-) (limited to 'fs/nfs/client.c') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index ba114faf195..906613362a5 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -581,7 +581,7 @@ static int nfs_init_server(struct nfs_server *server, struct nfs_client_initdata cl_init = { .hostname = data->nfs_server.hostname, .addr = (const struct sockaddr *)&data->nfs_server.address, - .addrlen = sizeof(data->nfs_server.address), + .addrlen = data->nfs_server.addrlen, .rpc_ops = &nfs_v2_clientops, }; struct nfs_client *clp; @@ -1018,7 +1018,7 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, error = nfs4_set_client(server, data->nfs_server.hostname, (struct sockaddr *)&data->nfs_server.address, - sizeof(data->nfs_server.address), + data->nfs_server.addrlen, data->client_address, data->auth_flavors[0], data->nfs_server.protocol, diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 75dd4e252ca..a8062119908 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -42,7 +42,8 @@ struct nfs_parsed_mount_data { char *client_address; struct { - struct sockaddr_in address; + struct sockaddr_storage address; + size_t addrlen; char *hostname; unsigned int version; unsigned short port; @@ -50,7 +51,8 @@ struct nfs_parsed_mount_data { } mount_server; struct { - struct sockaddr_in address; + struct sockaddr_storage address; + size_t addrlen; char *hostname; char *export_path; int protocol; diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 041fe9e9b74..7efc6a34b56 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -649,15 +649,18 @@ static int nfs_verify_server_address(struct sockaddr *addr) * to punt the mount. */ static void nfs_parse_server_address(char *value, - struct sockaddr *sap) + struct sockaddr *sap, + size_t *len) { struct sockaddr_in *ap = (void *)sap; ap->sin_family = AF_INET; + *len = sizeof(*ap); if (in4_pton(value, -1, (u8 *)&ap->sin_addr.s_addr, '\0', NULL)) return; sap->sa_family = AF_UNSPEC; + *len = 0; } /* @@ -984,7 +987,8 @@ static int nfs_parse_mount_options(char *raw, if (string == NULL) goto out_nomem; nfs_parse_server_address(string, (struct sockaddr *) - &mnt->nfs_server.address); + &mnt->nfs_server.address, + &mnt->nfs_server.addrlen); kfree(string); break; case Opt_clientaddr: @@ -1004,7 +1008,8 @@ static int nfs_parse_mount_options(char *raw, if (string == NULL) goto out_nomem; nfs_parse_server_address(string, (struct sockaddr *) - &mnt->mount_server.address); + &mnt->mount_server.address, + &mnt->mount_server.addrlen); kfree(string); break; @@ -1049,9 +1054,9 @@ out_unknown: static int nfs_try_mount(struct nfs_parsed_mount_data *args, struct nfs_fh *root_fh) { - struct sockaddr_in sin; - int status; + struct sockaddr *sap = (struct sockaddr *)&args->mount_server.address; char *hostname; + int status; if (args->mount_server.version == 0) { if (args->flags & NFS_MOUNT_VER3) @@ -1068,21 +1073,23 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args, /* * Construct the mount server's address. */ - if (args->mount_server.address.sin_addr.s_addr != INADDR_ANY) - sin = args->mount_server.address; - else - sin = args->nfs_server.address; + if (args->mount_server.address.ss_family == AF_UNSPEC) { + memcpy(sap, &args->nfs_server.address, + args->nfs_server.addrlen); + args->mount_server.addrlen = args->nfs_server.addrlen; + } + /* * autobind will be used if mount_server.port == 0 */ - nfs_set_port((struct sockaddr *)&sin, args->mount_server.port); + nfs_set_port(sap, args->mount_server.port); /* * Now ask the mount server to map our export path * to a file handle. */ - status = nfs_mount((struct sockaddr *) &sin, - sizeof(sin), + status = nfs_mount(sap, + args->mount_server.addrlen, hostname, args->nfs_server.export_path, args->mount_server.version, @@ -1165,9 +1172,6 @@ static int nfs_validate_mount_data(void *options, memset(mntfh->data + mntfh->size, 0, sizeof(mntfh->data) - mntfh->size); - if (!nfs_verify_server_address((struct sockaddr *) &data->addr)) - goto out_no_address; - /* * Translate to nfs_parsed_mount_data, which nfs_fill_super * can deal with. @@ -1182,7 +1186,14 @@ static int nfs_validate_mount_data(void *options, args->acregmax = data->acregmax; args->acdirmin = data->acdirmin; args->acdirmax = data->acdirmax; - args->nfs_server.address = data->addr; + + memcpy(&args->nfs_server.address, &data->addr, + sizeof(data->addr)); + args->nfs_server.addrlen = sizeof(data->addr); + if (!nfs_verify_server_address((struct sockaddr *) + &args->nfs_server.address)) + goto out_no_address; + if (!(data->flags & NFS_MOUNT_TCP)) args->nfs_server.protocol = XPRT_TRANSPORT_UDP; /* N.B. caller will free nfs_server.hostname in all cases */ @@ -1655,6 +1666,7 @@ static int nfs4_validate_mount_data(void *options, struct nfs_parsed_mount_data *args, const char *dev_name) { + struct sockaddr_in *ap; struct nfs4_mount_data *data = (struct nfs4_mount_data *)options; char *c; @@ -1675,11 +1687,13 @@ static int nfs4_validate_mount_data(void *options, switch (data->version) { case 1: - if (data->host_addrlen != sizeof(args->nfs_server.address)) + ap = (struct sockaddr_in *)&args->nfs_server.address; + if (data->host_addrlen > sizeof(args->nfs_server.address)) + goto out_no_address; + if (data->host_addrlen == 0) goto out_no_address; - if (copy_from_user(&args->nfs_server.address, - data->host_addr, - sizeof(args->nfs_server.address))) + args->nfs_server.addrlen = data->host_addrlen; + if (copy_from_user(ap, data->host_addr, data->host_addrlen)) return -EFAULT; if (!nfs_verify_server_address((struct sockaddr *) &args->nfs_server.address)) -- cgit v1.2.3-70-g09d2 From 7a3e3e18e40848b6f01d44407ce86b91b8535fbd Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 20 Dec 2007 16:03:57 -0500 Subject: NFS: Ensure that we respect NFS_MAX_TCP_TIMEOUT It isn't sufficient just to limit timeout->to_initval, we also need to limit to_maxval. Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'fs/nfs/client.c') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 906613362a5..59a6dccab54 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -387,12 +387,16 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, switch (proto) { case XPRT_TRANSPORT_TCP: case XPRT_TRANSPORT_RDMA: - if (!to->to_initval) + if (to->to_initval == 0) to->to_initval = 60 * HZ; if (to->to_initval > NFS_MAX_TCP_TIMEOUT) to->to_initval = NFS_MAX_TCP_TIMEOUT; to->to_increment = to->to_initval; to->to_maxval = to->to_initval + (to->to_increment * to->to_retries); + if (to->to_maxval > NFS_MAX_TCP_TIMEOUT) + to->to_maxval = NFS_MAX_TCP_TIMEOUT; + if (to->to_maxval < to->to_initval) + to->to_maxval = to->to_initval; to->to_exponential = 0; break; case XPRT_TRANSPORT_UDP: -- cgit v1.2.3-70-g09d2 From 331702337f2b2e7cef40366ee207a25604df4671 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 20 Dec 2007 16:03:59 -0500 Subject: NFS: Support per-mountpoint timeout parameters. Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 82 +++++++++++++++++++++++++++-------------------- fs/nfs/super.c | 4 +-- include/linux/nfs_fs_sb.h | 2 -- 3 files changed, 49 insertions(+), 39 deletions(-) (limited to 'fs/nfs/client.c') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 59a6dccab54..03d9bed7849 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -415,18 +415,16 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, * Create an RPC client handle */ static int nfs_create_rpc_client(struct nfs_client *clp, int proto, - unsigned int timeo, - unsigned int retrans, - rpc_authflavor_t flavor, - int flags) + const struct rpc_timeout *timeparms, + rpc_authflavor_t flavor, + int flags) { - struct rpc_timeout timeparms; struct rpc_clnt *clnt = NULL; struct rpc_create_args args = { .protocol = proto, .address = (struct sockaddr *)&clp->cl_addr, .addrsize = clp->cl_addrlen, - .timeout = &timeparms, + .timeout = timeparms, .servername = clp->cl_hostname, .program = &nfs_program, .version = clp->rpc_ops->version, @@ -437,10 +435,6 @@ static int nfs_create_rpc_client(struct nfs_client *clp, int proto, if (!IS_ERR(clp->cl_rpcclient)) return 0; - nfs_init_timeout_values(&timeparms, proto, timeo, retrans); - clp->retrans_timeo = timeparms.to_initval; - clp->retrans_count = timeparms.to_retries; - clnt = rpc_create(&args); if (IS_ERR(clnt)) { dprintk("%s: cannot create RPC client. Error = %ld\n", @@ -515,7 +509,9 @@ static inline void nfs_init_server_aclclient(struct nfs_server *server) /* * Create a general RPC client */ -static int nfs_init_server_rpcclient(struct nfs_server *server, rpc_authflavor_t pseudoflavour) +static int nfs_init_server_rpcclient(struct nfs_server *server, + const struct rpc_timeout *timeo, + rpc_authflavor_t pseudoflavour) { struct nfs_client *clp = server->nfs_client; @@ -525,6 +521,11 @@ static int nfs_init_server_rpcclient(struct nfs_server *server, rpc_authflavor_t return PTR_ERR(server->client); } + memcpy(&server->client->cl_timeout_default, + timeo, + sizeof(server->client->cl_timeout_default)); + server->client->cl_timeout = &server->client->cl_timeout_default; + if (pseudoflavour != clp->cl_rpcclient->cl_auth->au_flavor) { struct rpc_auth *auth; @@ -549,6 +550,7 @@ static int nfs_init_server_rpcclient(struct nfs_server *server, rpc_authflavor_t * Initialise an NFS2 or NFS3 client */ static int nfs_init_client(struct nfs_client *clp, + const struct rpc_timeout *timeparms, const struct nfs_parsed_mount_data *data) { int error; @@ -564,7 +566,7 @@ static int nfs_init_client(struct nfs_client *clp, * - RFC 2623, sec 2.3.2 */ error = nfs_create_rpc_client(clp, data->nfs_server.protocol, - data->timeo, data->retrans, RPC_AUTH_UNIX, 0); + timeparms, RPC_AUTH_UNIX, 0); if (error < 0) goto error; nfs_mark_client_ready(clp, NFS_CS_READY); @@ -588,6 +590,7 @@ static int nfs_init_server(struct nfs_server *server, .addrlen = data->nfs_server.addrlen, .rpc_ops = &nfs_v2_clientops, }; + struct rpc_timeout timeparms; struct nfs_client *clp; int error; @@ -605,7 +608,9 @@ static int nfs_init_server(struct nfs_server *server, return PTR_ERR(clp); } - error = nfs_init_client(clp, data); + nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, + data->timeo, data->retrans); + error = nfs_init_client(clp, &timeparms, data); if (error < 0) goto error; @@ -629,7 +634,7 @@ static int nfs_init_server(struct nfs_server *server, if (error < 0) goto error; - error = nfs_init_server_rpcclient(server, data->auth_flavors[0]); + error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]); if (error < 0) goto error; @@ -889,7 +894,8 @@ error: * Initialise an NFS4 client record */ static int nfs4_init_client(struct nfs_client *clp, - int proto, int timeo, int retrans, + int proto, + const struct rpc_timeout *timeparms, const char *ip_addr, rpc_authflavor_t authflavour) { @@ -904,7 +910,7 @@ static int nfs4_init_client(struct nfs_client *clp, /* Check NFS protocol revision and initialize RPC op vector */ clp->rpc_ops = &nfs_v4_clientops; - error = nfs_create_rpc_client(clp, proto, timeo, retrans, authflavour, + error = nfs_create_rpc_client(clp, proto, timeparms, authflavour, RPC_CLNT_CREATE_DISCRTRY); if (error < 0) goto error; @@ -936,7 +942,7 @@ static int nfs4_set_client(struct nfs_server *server, const size_t addrlen, const char *ip_addr, rpc_authflavor_t authflavour, - int proto, int timeo, int retrans) + int proto, const struct rpc_timeout *timeparms) { struct nfs_client_initdata cl_init = { .hostname = hostname, @@ -955,7 +961,7 @@ static int nfs4_set_client(struct nfs_server *server, error = PTR_ERR(clp); goto error; } - error = nfs4_init_client(clp, proto, timeo, retrans, ip_addr, authflavour); + error = nfs4_init_client(clp, proto, timeparms, ip_addr, authflavour); if (error < 0) goto error_put; @@ -976,10 +982,26 @@ error: static int nfs4_init_server(struct nfs_server *server, const struct nfs_parsed_mount_data *data) { + struct rpc_timeout timeparms; int error; dprintk("--> nfs4_init_server()\n"); + nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, + data->timeo, data->retrans); + + /* Get a client record */ + error = nfs4_set_client(server, + data->nfs_server.hostname, + (const struct sockaddr *)&data->nfs_server.address, + data->nfs_server.addrlen, + data->client_address, + data->auth_flavors[0], + data->nfs_server.protocol, + &timeparms); + if (error < 0) + goto error; + /* Initialise the client representation from the mount data */ server->flags = data->flags & NFS_MOUNT_FLAGMASK; server->caps |= NFS_CAP_ATOMIC_OPEN; @@ -994,8 +1016,9 @@ static int nfs4_init_server(struct nfs_server *server, server->acdirmin = data->acdirmin * HZ; server->acdirmax = data->acdirmax * HZ; - error = nfs_init_server_rpcclient(server, data->auth_flavors[0]); + error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]); +error: /* Done */ dprintk("<-- nfs4_init_server() = %d\n", error); return error; @@ -1018,18 +1041,6 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, if (!server) return ERR_PTR(-ENOMEM); - /* Get a client record */ - error = nfs4_set_client(server, - data->nfs_server.hostname, - (struct sockaddr *)&data->nfs_server.address, - data->nfs_server.addrlen, - data->client_address, - data->auth_flavors[0], - data->nfs_server.protocol, - data->timeo, data->retrans); - if (error < 0) - goto error; - /* set up the general RPC client */ error = nfs4_init_server(server, data); if (error < 0) @@ -1103,8 +1114,7 @@ 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_client->retrans_timeo, - parent_client->retrans_count); + parent_server->client->cl_timeout); if (error < 0) goto error; @@ -1112,7 +1122,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, nfs_server_copy_userdata(server, parent_server); server->caps |= NFS_CAP_ATOMIC_OPEN; - error = nfs_init_server_rpcclient(server, data->authflavor); + error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout, data->authflavor); if (error < 0) goto error; @@ -1181,7 +1191,9 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source, server->fsid = fattr->fsid; - error = nfs_init_server_rpcclient(server, source->client->cl_auth->au_flavor); + error = nfs_init_server_rpcclient(server, + source->client->cl_timeout, + source->client->cl_auth->au_flavor); if (error < 0) goto out_free_server; if (!IS_ERR(source->client_acl)) diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 3cbe32f3e88..0d1bc61d0b6 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -479,8 +479,8 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, } seq_printf(m, ",proto=%s", rpc_peeraddr2str(nfss->client, RPC_DISPLAY_PROTO)); - seq_printf(m, ",timeo=%lu", 10U * clp->retrans_timeo / HZ); - seq_printf(m, ",retrans=%u", clp->retrans_count); + seq_printf(m, ",timeo=%lu", 10U * nfss->client->cl_timeout->to_initval / HZ); + seq_printf(m, ",retrans=%u", nfss->client->cl_timeout->to_retries); seq_printf(m, ",sec=%s", nfs_pseudoflavour_to_name(nfss->client->cl_auth->au_flavor)); } diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 912cfe84ea8..d15c9487b8f 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -29,8 +29,6 @@ struct nfs_client { struct rpc_clnt * cl_rpcclient; const struct nfs_rpc_ops *rpc_ops; /* NFS protocol vector */ - unsigned long retrans_timeo; /* retransmit timeout */ - unsigned int retrans_count; /* number of retransmit tries */ #ifdef CONFIG_NFS_V4 u64 cl_clientid; /* constant */ -- cgit v1.2.3-70-g09d2 From 59dca3b28cb915745019d4f4c27d97b6b6ab12c6 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 3 Jan 2008 16:29:06 -0500 Subject: NFS: Fix the 'proto=' mount option Currently, if you have a server mounted using networking protocol, you cannot specify a different value using the 'proto=' option on another mountpoint. Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 20 +++++++++++++------- include/linux/nfs_fs_sb.h | 1 + 2 files changed, 14 insertions(+), 7 deletions(-) (limited to 'fs/nfs/client.c') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 03d9bed7849..18fcb05a070 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -100,6 +100,7 @@ struct nfs_client_initdata { const struct sockaddr *addr; size_t addrlen; const struct nfs_rpc_ops *rpc_ops; + int proto; }; /* @@ -138,6 +139,8 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_ INIT_LIST_HEAD(&clp->cl_superblocks); clp->cl_rpcclient = ERR_PTR(-EINVAL); + clp->cl_proto = cl_init->proto; + #ifdef CONFIG_NFS_V4 init_rwsem(&clp->cl_sem); INIT_LIST_HEAD(&clp->cl_delegations); @@ -289,6 +292,9 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat if (clp->rpc_ops != data->rpc_ops) continue; + if (clp->cl_proto != data->proto) + continue; + /* Match the full socket address */ if (memcmp(&clp->cl_addr, data->addr, sizeof(clp->cl_addr)) != 0) continue; @@ -414,14 +420,14 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, /* * Create an RPC client handle */ -static int nfs_create_rpc_client(struct nfs_client *clp, int proto, +static int nfs_create_rpc_client(struct nfs_client *clp, const struct rpc_timeout *timeparms, rpc_authflavor_t flavor, int flags) { struct rpc_clnt *clnt = NULL; struct rpc_create_args args = { - .protocol = proto, + .protocol = clp->cl_proto, .address = (struct sockaddr *)&clp->cl_addr, .addrsize = clp->cl_addrlen, .timeout = timeparms, @@ -565,8 +571,7 @@ static int nfs_init_client(struct nfs_client *clp, * Create a client RPC handle for doing FSSTAT with UNIX auth only * - RFC 2623, sec 2.3.2 */ - error = nfs_create_rpc_client(clp, data->nfs_server.protocol, - timeparms, RPC_AUTH_UNIX, 0); + error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX, 0); if (error < 0) goto error; nfs_mark_client_ready(clp, NFS_CS_READY); @@ -589,6 +594,7 @@ static int nfs_init_server(struct nfs_server *server, .addr = (const struct sockaddr *)&data->nfs_server.address, .addrlen = data->nfs_server.addrlen, .rpc_ops = &nfs_v2_clientops, + .proto = data->nfs_server.protocol, }; struct rpc_timeout timeparms; struct nfs_client *clp; @@ -894,7 +900,6 @@ error: * Initialise an NFS4 client record */ static int nfs4_init_client(struct nfs_client *clp, - int proto, const struct rpc_timeout *timeparms, const char *ip_addr, rpc_authflavor_t authflavour) @@ -910,7 +915,7 @@ static int nfs4_init_client(struct nfs_client *clp, /* Check NFS protocol revision and initialize RPC op vector */ clp->rpc_ops = &nfs_v4_clientops; - error = nfs_create_rpc_client(clp, proto, timeparms, authflavour, + error = nfs_create_rpc_client(clp, timeparms, authflavour, RPC_CLNT_CREATE_DISCRTRY); if (error < 0) goto error; @@ -949,6 +954,7 @@ static int nfs4_set_client(struct nfs_server *server, .addr = addr, .addrlen = addrlen, .rpc_ops = &nfs_v4_clientops, + .proto = proto, }; struct nfs_client *clp; int error; @@ -961,7 +967,7 @@ static int nfs4_set_client(struct nfs_server *server, error = PTR_ERR(clp); goto error; } - error = nfs4_init_client(clp, proto, timeparms, ip_addr, authflavour); + error = nfs4_init_client(clp, timeparms, ip_addr, authflavour); if (error < 0) goto error_put; diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index d15c9487b8f..b5ba5f79485 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -29,6 +29,7 @@ struct nfs_client { struct rpc_clnt * cl_rpcclient; const struct nfs_rpc_ops *rpc_ops; /* NFS protocol vector */ + int cl_proto; /* Network transport protocol */ #ifdef CONFIG_NFS_V4 u64 cl_clientid; /* constant */ -- cgit v1.2.3-70-g09d2 From 9289e7f91add1c09c3ec8571a2080f7507730b8d Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 11 Jan 2008 17:09:52 -0500 Subject: NFS: Invoke nlmclnt_init during NFS mount processing Cache an appropriate nlm_host structure in the NFS client's mount point metadata for later use. Note that there is no need to set NFS_MOUNT_NONLM in the error case -- if nfs_start_lockd() returns a non-zero value, its callers ensure that the mount request fails outright. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 32 +++++++++++++++++++------------- include/linux/nfs_fs_sb.h | 2 ++ 2 files changed, 21 insertions(+), 13 deletions(-) (limited to 'fs/nfs/client.c') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 18fcb05a070..0b3ce86f6fc 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -458,7 +458,7 @@ static int nfs_create_rpc_client(struct nfs_client *clp, static void nfs_destroy_server(struct nfs_server *server) { if (!(server->flags & NFS_MOUNT_NONLM)) - lockd_down(); /* release rpc.lockd */ + nlmclnt_done(server->nlm_host); } /* @@ -466,20 +466,26 @@ static void nfs_destroy_server(struct nfs_server *server) */ static int nfs_start_lockd(struct nfs_server *server) { - int error = 0; + struct nlm_host *host; + struct nfs_client *clp = server->nfs_client; + u32 nfs_version = clp->rpc_ops->version; + unsigned short protocol = server->flags & NFS_MOUNT_TCP ? + IPPROTO_TCP : IPPROTO_UDP; - if (server->nfs_client->rpc_ops->version > 3) - goto out; + if (nfs_version > 3) + return 0; if (server->flags & NFS_MOUNT_NONLM) - goto out; - error = lockd_up((server->flags & NFS_MOUNT_TCP) ? - IPPROTO_TCP : IPPROTO_UDP); - if (error < 0) - server->flags |= NFS_MOUNT_NONLM; - else - server->destroy = nfs_destroy_server; -out: - return error; + return 0; + + host = nlmclnt_init(clp->cl_hostname, + (struct sockaddr *)&clp->cl_addr, + clp->cl_addrlen, protocol, nfs_version); + if (IS_ERR(host)) + return PTR_ERR(host); + + server->nlm_host = host; + server->destroy = nfs_destroy_server; + return 0; } /* diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index b5ba5f79485..3423c6761bf 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -8,6 +8,7 @@ #include struct nfs_iostats; +struct nlm_host; /* * The nfs_client identifies our client state to the server. @@ -80,6 +81,7 @@ struct nfs_server { struct list_head master_link; /* link in master servers list */ struct rpc_clnt * client; /* RPC client handle */ struct rpc_clnt * client_acl; /* ACL RPC client handle */ + struct nlm_host *nlm_host; /* NLM client handle */ struct nfs_iostats * io_stats; /* I/O statistics */ struct backing_dev_info backing_dev_info; atomic_long_t writeback; /* number of writeback pages */ -- cgit v1.2.3-70-g09d2 From 883bb163f84e0a54b29846c61621f52db3f27393 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 15 Jan 2008 16:04:20 -0500 Subject: NLM: Introduce an arguments structure for nlmclnt_init() Clean up: pass 5 arguments to nlmclnt_init() in a structure similar to the new nfs_client_initdata structure. Signed-off-by: Chuck Lever --- fs/lockd/clntlock.c | 22 ++++++++-------------- fs/nfs/client.c | 17 ++++++++++------- include/linux/lockd/bind.h | 19 ++++++++++++++----- 3 files changed, 32 insertions(+), 26 deletions(-) (limited to 'fs/nfs/client.c') diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index 9a8f4f45c19..0b45fd3a4bf 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -43,31 +43,25 @@ static LIST_HEAD(nlm_blocked); /** * nlmclnt_init - Set up per-NFS mount point lockd data structures - * @server_name: server's hostname - * @server_address: server's network address - * @server_addrlen: length of server's address - * @protocol: transport protocol lockd should use - * @nfs_version: NFS protocol version for this mount point + * @nlm_init: pointer to arguments structure * * Returns pointer to an appropriate nlm_host struct, * or an ERR_PTR value. */ -struct nlm_host *nlmclnt_init(const char *server_name, - const struct sockaddr *server_address, - size_t server_addrlen, - unsigned short protocol, u32 nfs_version) +struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init) { struct nlm_host *host; - u32 nlm_version = (nfs_version == 2) ? 1 : 4; + u32 nlm_version = (nlm_init->nfs_version == 2) ? 1 : 4; int status; - status = lockd_up(protocol); + status = lockd_up(nlm_init->protocol); if (status < 0) return ERR_PTR(status); - host = nlmclnt_lookup_host((struct sockaddr_in *)server_address, - protocol, nlm_version, - server_name, strlen(server_name)); + host = nlmclnt_lookup_host((struct sockaddr_in *)nlm_init->address, + nlm_init->protocol, nlm_version, + nlm_init->hostname, + strlen(nlm_init->hostname)); if (host == NULL) { lockd_down(); return ERR_PTR(-ENOLCK); diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 0b3ce86f6fc..7a15832369e 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -468,18 +468,21 @@ static int nfs_start_lockd(struct nfs_server *server) { struct nlm_host *host; struct nfs_client *clp = server->nfs_client; - u32 nfs_version = clp->rpc_ops->version; - unsigned short protocol = server->flags & NFS_MOUNT_TCP ? - IPPROTO_TCP : IPPROTO_UDP; + struct nlmclnt_initdata nlm_init = { + .hostname = clp->cl_hostname, + .address = (struct sockaddr *)&clp->cl_addr, + .addrlen = clp->cl_addrlen, + .protocol = server->flags & NFS_MOUNT_TCP ? + IPPROTO_TCP : IPPROTO_UDP, + .nfs_version = clp->rpc_ops->version, + }; - if (nfs_version > 3) + if (nlm_init.nfs_version > 3) return 0; if (server->flags & NFS_MOUNT_NONLM) return 0; - host = nlmclnt_init(clp->cl_hostname, - (struct sockaddr *)&clp->cl_addr, - clp->cl_addrlen, protocol, nfs_version); + host = nlmclnt_init(&nlm_init); if (IS_ERR(host)) return PTR_ERR(host); diff --git a/include/linux/lockd/bind.h b/include/linux/lockd/bind.h index 73368075af0..3d25bcd139d 100644 --- a/include/linux/lockd/bind.h +++ b/include/linux/lockd/bind.h @@ -32,14 +32,23 @@ struct nlmsvc_binding { extern struct nlmsvc_binding * nlmsvc_ops; +/* + * Similar to nfs_client_initdata, but without the NFS-specific + * rpc_ops field. + */ +struct nlmclnt_initdata { + const char *hostname; + const struct sockaddr *address; + size_t addrlen; + unsigned short protocol; + u32 nfs_version; +}; + /* * Functions exported by the lockd module */ -extern struct nlm_host *nlmclnt_init(const char *server_name, - const struct sockaddr *server_address, - size_t server_addrlen, - unsigned short protocol, - u32 nfs_version); + +extern struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init); extern void nlmclnt_done(struct nlm_host *host); extern int nlmclnt_proc(struct nlm_host *host, int cmd, -- cgit v1.2.3-70-g09d2 From 3fbd67ad61f6d5a09ea717b56c50bc5c3d8042a8 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 26 Jan 2008 01:06:40 -0500 Subject: NFSv4: Iterate through all nfs_clients when the server recalls a delegation The same delegation may have been handed out to more than one nfs_client. Ensure that if a recall occurs, we return all instances. Signed-off-by: Trond Myklebust --- fs/nfs/callback_proc.c | 39 ++++++++++++++++++++++----------------- fs/nfs/client.c | 35 +++++++++++++++++++++++++++++++++++ fs/nfs/internal.h | 1 + 3 files changed, 58 insertions(+), 17 deletions(-) (limited to 'fs/nfs/client.c') diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index e89a9007c91..15f7785048d 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -75,23 +75,28 @@ __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy) dprintk("NFS: RECALL callback request from %s\n", rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR)); - inode = nfs_delegation_find_inode(clp, &args->fh); - if (inode == NULL) - goto out_putclient; - /* Set up a helper thread to actually return the delegation */ - switch(nfs_async_inode_return_delegation(inode, &args->stateid)) { - case 0: - res = 0; - break; - case -ENOENT: - res = htonl(NFS4ERR_BAD_STATEID); - break; - default: - res = htonl(NFS4ERR_RESOURCE); - } - iput(inode); -out_putclient: - nfs_put_client(clp); + do { + struct nfs_client *prev = clp; + + inode = nfs_delegation_find_inode(clp, &args->fh); + if (inode != NULL) { + /* Set up a helper thread to actually return the delegation */ + switch(nfs_async_inode_return_delegation(inode, &args->stateid)) { + case 0: + res = 0; + break; + case -ENOENT: + if (res != 0) + res = htonl(NFS4ERR_BAD_STATEID); + break; + default: + res = htonl(NFS4ERR_RESOURCE); + } + iput(inode); + } + clp = nfs_find_client_next(prev); + nfs_put_client(prev); + } while (clp != NULL); out: dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(res)); return res; diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 7a15832369e..685c43f810c 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -275,6 +275,41 @@ struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion) return NULL; } +/* + * Find a client by IP address and protocol version + * - returns NULL if no such client + */ +struct nfs_client *nfs_find_client_next(struct nfs_client *clp) +{ + struct sockaddr *sap = (struct sockaddr *)&clp->cl_addr; + u32 nfsvers = clp->rpc_ops->version; + + spin_lock(&nfs_client_lock); + list_for_each_entry_continue(clp, &nfs_client_list, cl_share_link) { + 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) + continue; + + /* Different NFS versions cannot share the same nfs_client */ + if (clp->rpc_ops->version != nfsvers) + continue; + + if (sap->sa_family != clap->sa_family) + continue; + /* Match only the IP address, not the port number */ + if (!nfs_sockaddr_match_ipaddr(sap, clap)) + continue; + + atomic_inc(&clp->cl_count); + spin_unlock(&nfs_client_lock); + return clp; + } + spin_unlock(&nfs_client_lock); + return NULL; +} + /* * Find an nfs_client on the list that matches the initialisation data * that is supplied. diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index a8062119908..0f5619611b8 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -64,6 +64,7 @@ extern struct rpc_program nfs_program; extern void nfs_put_client(struct nfs_client *); extern struct nfs_client *nfs_find_client(const struct sockaddr *, u32); +extern struct nfs_client *nfs_find_client_next(struct nfs_client *); extern struct nfs_server *nfs_create_server( const struct nfs_parsed_mount_data *, struct nfs_fh *); -- cgit v1.2.3-70-g09d2