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 +++ 1 file changed, 3 insertions(+) (limited to 'fs/nfs/client.c') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index a6f625497612..c3740f5ab978 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); -- cgit 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 c3740f5ab978..8b5f9b9685dd 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 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 8b5f9b9685dd..d7f6d50442b7 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 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 d7f6d50442b7..ff778ecee0bd 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 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 ff778ecee0bd..3b21731ae571 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 acfc56f9edc0..be4ce1c3a3d8 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 a3492d6f8f9b..5608e6a4c1e1 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 9f949b587684..97b325756667 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 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 3b21731ae571..701cd193a014 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 11833f4caeaa..b03dcd8403f1 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 bf94c6e0a503..f9c7432471dc 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 5608e6a4c1e1..75f3cbf922a3 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 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 701cd193a014..876162cddf1e 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 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 876162cddf1e..44fe7fd7bfbf 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 0b3dcba3d97d..912cfe84ea86 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 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 bbf67f148ff9..9b6bbf1b9787 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 fa9586dcc3dd..e89a9007c91c 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 44fe7fd7bfbf..73bf4ecad030 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 058d503a0ee1..c8458b168018 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 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 73bf4ecad030..e43072bdbb0c 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 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 e43072bdbb0c..11380601fc78 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 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 11380601fc78..ba114faf195f 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 c8458b168018..75dd4e252cae 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 bd1b1617905d..5f9ba41ed5bf 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 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 ba114faf195f..906613362a56 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 75dd4e252cae..a80621199086 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 041fe9e9b74d..7efc6a34b56b 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 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 906613362a56..59a6dccab548 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 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 59a6dccab548..03d9bed7849a 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 3cbe32f3e88b..0d1bc61d0b68 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 912cfe84ea86..d15c9487b8f1 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 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 03d9bed7849a..18fcb05a0707 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 d15c9487b8f1..b5ba5f79485d 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 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 18fcb05a0707..0b3ce86f6fc9 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 b5ba5f79485d..3423c6761bf7 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 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 9a8f4f45c19e..0b45fd3a4bfd 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 0b3ce86f6fc9..7a15832369e9 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 73368075af03..3d25bcd139d1 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 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 e89a9007c91c..15f7785048d3 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 7a15832369e9..685c43f810c1 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 a80621199086..0f5619611b8d 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