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/delegation.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'fs/nfs/delegation.c') 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); -- cgit v1.2.2 From e6f810759505bc86c009854b82cc495ffd8eb020 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 24 Jan 2008 18:14:34 -0500 Subject: NFS: Add an asynchronous delegreturn operation for use in nfs_clear_inode Otherwise, there is a potential deadlock if the last dput() from an NFSv4 close() or other asynchronous operation leads to nfs_clear_inode calling the synchronous delegreturn. Signed-off-by: Trond Myklebust --- fs/nfs/delegation.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) (limited to 'fs/nfs/delegation.c') diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index b03dcd8403f1..2dead8d1dd55 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -174,11 +174,11 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct return status; } -static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation) +static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) { int res = 0; - res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid); + res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync); nfs_free_delegation(delegation); return res; } @@ -208,7 +208,7 @@ static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegat up_read(&clp->cl_sem); nfs_msync_inode(inode); - return nfs_do_return_delegation(inode, delegation); + return nfs_do_return_delegation(inode, delegation, 1); } static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid) @@ -228,6 +228,27 @@ nomatch: return NULL; } +/* + * This function returns the delegation without reclaiming opens + * or protecting against delegation reclaims. + * It is therefore really only safe to be called from + * nfs4_clear_inode() + */ +void nfs_inode_return_delegation_noreclaim(struct inode *inode) +{ + struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; + struct nfs_inode *nfsi = NFS_I(inode); + struct nfs_delegation *delegation; + + if (rcu_dereference(nfsi->delegation) != NULL) { + spin_lock(&clp->cl_lock); + delegation = nfs_detach_delegation_locked(nfsi, NULL); + spin_unlock(&clp->cl_lock); + if (delegation != NULL) + nfs_do_return_delegation(inode, delegation, 0); + } +} + int nfs_inode_return_delegation(struct inode *inode) { struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; @@ -388,7 +409,7 @@ static int recall_thread(void *data) nfs_msync_inode(inode); if (delegation != NULL) - nfs_do_return_delegation(inode, delegation); + nfs_do_return_delegation(inode, delegation, 1); iput(inode); module_put_and_exit(0); } -- cgit v1.2.2 From 57bfa89171e50cddf51a4f62c90e47c6259857b4 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 25 Jan 2008 16:38:18 -0500 Subject: NFSv4: Deal more correctly with duplicate delegations If a (broken?) server hands out two different delegations for the same file, then we should return one of them. Signed-off-by: Trond Myklebust --- fs/nfs/delegation.c | 89 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 38 deletions(-) (limited to 'fs/nfs/delegation.c') diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 2dead8d1dd55..b9eadd18ba70 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -125,6 +125,32 @@ void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, st put_rpccred(oldcred); } +static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) +{ + int res = 0; + + res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync); + nfs_free_delegation(delegation); + return res; +} + +static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid) +{ + struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); + + if (delegation == NULL) + goto nomatch; + if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data, + sizeof(delegation->stateid.data)) != 0) + goto nomatch; + list_del_rcu(&delegation->super_list); + nfsi->delegation_state = 0; + rcu_assign_pointer(nfsi->delegation, NULL); + return delegation; +nomatch: + return NULL; +} + /* * Set up a delegation on an inode */ @@ -133,6 +159,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; struct nfs_inode *nfsi = NFS_I(inode); struct nfs_delegation *delegation; + struct nfs_delegation *freeme = NULL; int status = 0; delegation = kmalloc(sizeof(*delegation), GFP_KERNEL); @@ -147,42 +174,45 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct delegation->inode = inode; spin_lock(&clp->cl_lock); - if (rcu_dereference(nfsi->delegation) == NULL) { - list_add_rcu(&delegation->super_list, &clp->cl_delegations); - nfsi->delegation_state = delegation->type; - rcu_assign_pointer(nfsi->delegation, delegation); - delegation = NULL; - } else { + if (rcu_dereference(nfsi->delegation) != NULL) { if (memcmp(&delegation->stateid, &nfsi->delegation->stateid, - sizeof(delegation->stateid)) != 0 || - delegation->type != nfsi->delegation->type) { - printk(KERN_WARNING "%s: server %s handed out " - "a duplicate delegation!\n", - __FUNCTION__, clp->cl_hostname); - status = -EIO; + sizeof(delegation->stateid)) == 0 && + delegation->type == nfsi->delegation->type) { + goto out; + } + /* + * Deal with broken servers that hand out two + * delegations for the same file. + */ + dfprintk(FILE, "%s: server %s handed out " + "a duplicate delegation!\n", + __FUNCTION__, clp->cl_hostname); + if (delegation->type <= nfsi->delegation->type) { + freeme = delegation; + delegation = NULL; + goto out; } + freeme = nfs_detach_delegation_locked(nfsi, NULL); } + list_add_rcu(&delegation->super_list, &clp->cl_delegations); + nfsi->delegation_state = delegation->type; + rcu_assign_pointer(nfsi->delegation, delegation); + delegation = NULL; /* Ensure we revalidate the attributes and page cache! */ spin_lock(&inode->i_lock); nfsi->cache_validity |= NFS_INO_REVAL_FORCED; spin_unlock(&inode->i_lock); +out: spin_unlock(&clp->cl_lock); if (delegation != NULL) nfs_free_delegation(delegation); + if (freeme != NULL) + nfs_do_return_delegation(inode, freeme, 0); return status; } -static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) -{ - int res = 0; - - res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync); - nfs_free_delegation(delegation); - return res; -} - /* Sync all data to disk upon delegation return */ static void nfs_msync_inode(struct inode *inode) { @@ -211,23 +241,6 @@ static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegat return nfs_do_return_delegation(inode, delegation, 1); } -static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid) -{ - struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); - - if (delegation == NULL) - goto nomatch; - if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data, - sizeof(delegation->stateid.data)) != 0) - goto nomatch; - list_del_rcu(&delegation->super_list); - nfsi->delegation_state = 0; - rcu_assign_pointer(nfsi->delegation, NULL); - return delegation; -nomatch: - return NULL; -} - /* * This function returns the delegation without reclaiming opens * or protecting against delegation reclaims. -- cgit v1.2.2