diff options
| -rw-r--r-- | fs/nfs/delegation.c | 89 |
1 files changed, 51 insertions, 38 deletions
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 | |||
| 125 | put_rpccred(oldcred); | 125 | put_rpccred(oldcred); |
| 126 | } | 126 | } |
| 127 | 127 | ||
| 128 | static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) | ||
| 129 | { | ||
| 130 | int res = 0; | ||
| 131 | |||
| 132 | res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync); | ||
| 133 | nfs_free_delegation(delegation); | ||
| 134 | return res; | ||
| 135 | } | ||
| 136 | |||
| 137 | static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid) | ||
| 138 | { | ||
| 139 | struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); | ||
| 140 | |||
| 141 | if (delegation == NULL) | ||
| 142 | goto nomatch; | ||
| 143 | if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data, | ||
| 144 | sizeof(delegation->stateid.data)) != 0) | ||
| 145 | goto nomatch; | ||
| 146 | list_del_rcu(&delegation->super_list); | ||
| 147 | nfsi->delegation_state = 0; | ||
| 148 | rcu_assign_pointer(nfsi->delegation, NULL); | ||
| 149 | return delegation; | ||
| 150 | nomatch: | ||
| 151 | return NULL; | ||
| 152 | } | ||
| 153 | |||
| 128 | /* | 154 | /* |
| 129 | * Set up a delegation on an inode | 155 | * Set up a delegation on an inode |
| 130 | */ | 156 | */ |
| @@ -133,6 +159,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct | |||
| 133 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; | 159 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; |
| 134 | struct nfs_inode *nfsi = NFS_I(inode); | 160 | struct nfs_inode *nfsi = NFS_I(inode); |
| 135 | struct nfs_delegation *delegation; | 161 | struct nfs_delegation *delegation; |
| 162 | struct nfs_delegation *freeme = NULL; | ||
| 136 | int status = 0; | 163 | int status = 0; |
| 137 | 164 | ||
| 138 | delegation = kmalloc(sizeof(*delegation), GFP_KERNEL); | 165 | delegation = kmalloc(sizeof(*delegation), GFP_KERNEL); |
| @@ -147,42 +174,45 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct | |||
| 147 | delegation->inode = inode; | 174 | delegation->inode = inode; |
| 148 | 175 | ||
| 149 | spin_lock(&clp->cl_lock); | 176 | spin_lock(&clp->cl_lock); |
| 150 | if (rcu_dereference(nfsi->delegation) == NULL) { | 177 | if (rcu_dereference(nfsi->delegation) != NULL) { |
| 151 | list_add_rcu(&delegation->super_list, &clp->cl_delegations); | ||
| 152 | nfsi->delegation_state = delegation->type; | ||
| 153 | rcu_assign_pointer(nfsi->delegation, delegation); | ||
| 154 | delegation = NULL; | ||
| 155 | } else { | ||
| 156 | if (memcmp(&delegation->stateid, &nfsi->delegation->stateid, | 178 | if (memcmp(&delegation->stateid, &nfsi->delegation->stateid, |
| 157 | sizeof(delegation->stateid)) != 0 || | 179 | sizeof(delegation->stateid)) == 0 && |
| 158 | delegation->type != nfsi->delegation->type) { | 180 | delegation->type == nfsi->delegation->type) { |
| 159 | printk(KERN_WARNING "%s: server %s handed out " | 181 | goto out; |
| 160 | "a duplicate delegation!\n", | 182 | } |
| 161 | __FUNCTION__, clp->cl_hostname); | 183 | /* |
| 162 | status = -EIO; | 184 | * Deal with broken servers that hand out two |
| 185 | * delegations for the same file. | ||
| 186 | */ | ||
| 187 | dfprintk(FILE, "%s: server %s handed out " | ||
| 188 | "a duplicate delegation!\n", | ||
| 189 | __FUNCTION__, clp->cl_hostname); | ||
| 190 | if (delegation->type <= nfsi->delegation->type) { | ||
| 191 | freeme = delegation; | ||
| 192 | delegation = NULL; | ||
| 193 | goto out; | ||
| 163 | } | 194 | } |
| 195 | freeme = nfs_detach_delegation_locked(nfsi, NULL); | ||
| 164 | } | 196 | } |
| 197 | list_add_rcu(&delegation->super_list, &clp->cl_delegations); | ||
| 198 | nfsi->delegation_state = delegation->type; | ||
| 199 | rcu_assign_pointer(nfsi->delegation, delegation); | ||
| 200 | delegation = NULL; | ||
| 165 | 201 | ||
| 166 | /* Ensure we revalidate the attributes and page cache! */ | 202 | /* Ensure we revalidate the attributes and page cache! */ |
| 167 | spin_lock(&inode->i_lock); | 203 | spin_lock(&inode->i_lock); |
| 168 | nfsi->cache_validity |= NFS_INO_REVAL_FORCED; | 204 | nfsi->cache_validity |= NFS_INO_REVAL_FORCED; |
| 169 | spin_unlock(&inode->i_lock); | 205 | spin_unlock(&inode->i_lock); |
| 170 | 206 | ||
| 207 | out: | ||
| 171 | spin_unlock(&clp->cl_lock); | 208 | spin_unlock(&clp->cl_lock); |
| 172 | if (delegation != NULL) | 209 | if (delegation != NULL) |
| 173 | nfs_free_delegation(delegation); | 210 | nfs_free_delegation(delegation); |
| 211 | if (freeme != NULL) | ||
| 212 | nfs_do_return_delegation(inode, freeme, 0); | ||
| 174 | return status; | 213 | return status; |
| 175 | } | 214 | } |
| 176 | 215 | ||
| 177 | static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) | ||
| 178 | { | ||
| 179 | int res = 0; | ||
| 180 | |||
| 181 | res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync); | ||
| 182 | nfs_free_delegation(delegation); | ||
| 183 | return res; | ||
| 184 | } | ||
| 185 | |||
| 186 | /* Sync all data to disk upon delegation return */ | 216 | /* Sync all data to disk upon delegation return */ |
| 187 | static void nfs_msync_inode(struct inode *inode) | 217 | static void nfs_msync_inode(struct inode *inode) |
| 188 | { | 218 | { |
| @@ -211,23 +241,6 @@ static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegat | |||
| 211 | return nfs_do_return_delegation(inode, delegation, 1); | 241 | return nfs_do_return_delegation(inode, delegation, 1); |
| 212 | } | 242 | } |
| 213 | 243 | ||
| 214 | static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid) | ||
| 215 | { | ||
| 216 | struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); | ||
| 217 | |||
| 218 | if (delegation == NULL) | ||
| 219 | goto nomatch; | ||
| 220 | if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data, | ||
| 221 | sizeof(delegation->stateid.data)) != 0) | ||
| 222 | goto nomatch; | ||
| 223 | list_del_rcu(&delegation->super_list); | ||
| 224 | nfsi->delegation_state = 0; | ||
| 225 | rcu_assign_pointer(nfsi->delegation, NULL); | ||
| 226 | return delegation; | ||
| 227 | nomatch: | ||
| 228 | return NULL; | ||
| 229 | } | ||
| 230 | |||
| 231 | /* | 244 | /* |
| 232 | * This function returns the delegation without reclaiming opens | 245 | * This function returns the delegation without reclaiming opens |
| 233 | * or protecting against delegation reclaims. | 246 | * or protecting against delegation reclaims. |
