diff options
| -rw-r--r-- | fs/nfs/dir.c | 14 | ||||
| -rw-r--r-- | fs/nfs/inode.c | 3 | ||||
| -rw-r--r-- | fs/nfs/nfs4proc.c | 6 | ||||
| -rw-r--r-- | fs/nfs/unlink.c | 114 | ||||
| -rw-r--r-- | include/linux/nfs_fs.h | 8 |
5 files changed, 127 insertions, 18 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 8ec7fbd8240c..35334539d947 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
| @@ -562,6 +562,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
| 562 | nfs_fattr_init(&fattr); | 562 | nfs_fattr_init(&fattr); |
| 563 | desc->entry = &my_entry; | 563 | desc->entry = &my_entry; |
| 564 | 564 | ||
| 565 | nfs_block_sillyrename(dentry); | ||
| 565 | while(!desc->entry->eof) { | 566 | while(!desc->entry->eof) { |
| 566 | res = readdir_search_pagecache(desc); | 567 | res = readdir_search_pagecache(desc); |
| 567 | 568 | ||
| @@ -592,6 +593,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
| 592 | break; | 593 | break; |
| 593 | } | 594 | } |
| 594 | } | 595 | } |
| 596 | nfs_unblock_sillyrename(dentry); | ||
| 595 | unlock_kernel(); | 597 | unlock_kernel(); |
| 596 | if (res > 0) | 598 | if (res > 0) |
| 597 | res = 0; | 599 | res = 0; |
| @@ -866,6 +868,7 @@ struct dentry_operations nfs_dentry_operations = { | |||
| 866 | static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) | 868 | static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) |
| 867 | { | 869 | { |
| 868 | struct dentry *res; | 870 | struct dentry *res; |
| 871 | struct dentry *parent; | ||
| 869 | struct inode *inode = NULL; | 872 | struct inode *inode = NULL; |
| 870 | int error; | 873 | int error; |
| 871 | struct nfs_fh fhandle; | 874 | struct nfs_fh fhandle; |
| @@ -894,26 +897,31 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru | |||
| 894 | goto out_unlock; | 897 | goto out_unlock; |
| 895 | } | 898 | } |
| 896 | 899 | ||
| 900 | parent = dentry->d_parent; | ||
| 901 | /* Protect against concurrent sillydeletes */ | ||
| 902 | nfs_block_sillyrename(parent); | ||
| 897 | error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr); | 903 | error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr); |
| 898 | if (error == -ENOENT) | 904 | if (error == -ENOENT) |
| 899 | goto no_entry; | 905 | goto no_entry; |
| 900 | if (error < 0) { | 906 | if (error < 0) { |
| 901 | res = ERR_PTR(error); | 907 | res = ERR_PTR(error); |
| 902 | goto out_unlock; | 908 | goto out_unblock_sillyrename; |
| 903 | } | 909 | } |
| 904 | inode = nfs_fhget(dentry->d_sb, &fhandle, &fattr); | 910 | inode = nfs_fhget(dentry->d_sb, &fhandle, &fattr); |
| 905 | res = (struct dentry *)inode; | 911 | res = (struct dentry *)inode; |
| 906 | if (IS_ERR(res)) | 912 | if (IS_ERR(res)) |
| 907 | goto out_unlock; | 913 | goto out_unblock_sillyrename; |
| 908 | 914 | ||
| 909 | no_entry: | 915 | no_entry: |
| 910 | res = d_materialise_unique(dentry, inode); | 916 | res = d_materialise_unique(dentry, inode); |
| 911 | if (res != NULL) { | 917 | if (res != NULL) { |
| 912 | if (IS_ERR(res)) | 918 | if (IS_ERR(res)) |
| 913 | goto out_unlock; | 919 | goto out_unblock_sillyrename; |
| 914 | dentry = res; | 920 | dentry = res; |
| 915 | } | 921 | } |
| 916 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); | 922 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); |
| 923 | out_unblock_sillyrename: | ||
| 924 | nfs_unblock_sillyrename(parent); | ||
| 917 | out_unlock: | 925 | out_unlock: |
| 918 | unlock_kernel(); | 926 | unlock_kernel(); |
| 919 | out: | 927 | out: |
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 6d2f2a3eccf8..173e294dffc5 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
| @@ -1169,6 +1169,9 @@ static void init_once(struct kmem_cache * cachep, void *foo) | |||
| 1169 | INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC); | 1169 | INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC); |
| 1170 | nfsi->ncommit = 0; | 1170 | nfsi->ncommit = 0; |
| 1171 | nfsi->npages = 0; | 1171 | nfsi->npages = 0; |
| 1172 | atomic_set(&nfsi->silly_count, 1); | ||
| 1173 | INIT_HLIST_HEAD(&nfsi->silly_list); | ||
| 1174 | init_waitqueue_head(&nfsi->waitqueue); | ||
| 1172 | nfs4_init_once(nfsi); | 1175 | nfs4_init_once(nfsi); |
| 1173 | } | 1176 | } |
| 1174 | 1177 | ||
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index cb99fd90a9ac..2cb3b8b71ee7 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
| @@ -1372,6 +1372,7 @@ out_close: | |||
| 1372 | struct dentry * | 1372 | struct dentry * |
| 1373 | nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd) | 1373 | nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd) |
| 1374 | { | 1374 | { |
| 1375 | struct dentry *parent; | ||
| 1375 | struct path path = { | 1376 | struct path path = { |
| 1376 | .mnt = nd->mnt, | 1377 | .mnt = nd->mnt, |
| 1377 | .dentry = dentry, | 1378 | .dentry = dentry, |
| @@ -1394,6 +1395,9 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd) | |||
| 1394 | cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0); | 1395 | cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0); |
| 1395 | if (IS_ERR(cred)) | 1396 | if (IS_ERR(cred)) |
| 1396 | return (struct dentry *)cred; | 1397 | return (struct dentry *)cred; |
| 1398 | parent = dentry->d_parent; | ||
| 1399 | /* Protect against concurrent sillydeletes */ | ||
| 1400 | nfs_block_sillyrename(parent); | ||
| 1397 | state = nfs4_do_open(dir, &path, nd->intent.open.flags, &attr, cred); | 1401 | state = nfs4_do_open(dir, &path, nd->intent.open.flags, &attr, cred); |
| 1398 | put_rpccred(cred); | 1402 | put_rpccred(cred); |
| 1399 | if (IS_ERR(state)) { | 1403 | if (IS_ERR(state)) { |
| @@ -1401,12 +1405,14 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd) | |||
| 1401 | d_add(dentry, NULL); | 1405 | d_add(dentry, NULL); |
| 1402 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); | 1406 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); |
| 1403 | } | 1407 | } |
| 1408 | nfs_unblock_sillyrename(parent); | ||
| 1404 | return (struct dentry *)state; | 1409 | return (struct dentry *)state; |
| 1405 | } | 1410 | } |
| 1406 | res = d_add_unique(dentry, igrab(state->inode)); | 1411 | res = d_add_unique(dentry, igrab(state->inode)); |
| 1407 | if (res != NULL) | 1412 | if (res != NULL) |
| 1408 | path.dentry = res; | 1413 | path.dentry = res; |
| 1409 | nfs_set_verifier(path.dentry, nfs_save_change_attribute(dir)); | 1414 | nfs_set_verifier(path.dentry, nfs_save_change_attribute(dir)); |
| 1415 | nfs_unblock_sillyrename(parent); | ||
| 1410 | nfs4_intent_set_file(nd, &path, state); | 1416 | nfs4_intent_set_file(nd, &path, state); |
| 1411 | return res; | 1417 | return res; |
| 1412 | } | 1418 | } |
diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index 1aed850d18f2..6ecd46c967c8 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | 14 | ||
| 15 | 15 | ||
| 16 | struct nfs_unlinkdata { | 16 | struct nfs_unlinkdata { |
| 17 | struct hlist_node list; | ||
| 17 | struct nfs_removeargs args; | 18 | struct nfs_removeargs args; |
| 18 | struct nfs_removeres res; | 19 | struct nfs_removeres res; |
| 19 | struct inode *dir; | 20 | struct inode *dir; |
| @@ -52,6 +53,20 @@ static int nfs_copy_dname(struct dentry *dentry, struct nfs_unlinkdata *data) | |||
| 52 | return 0; | 53 | return 0; |
| 53 | } | 54 | } |
| 54 | 55 | ||
| 56 | static void nfs_free_dname(struct nfs_unlinkdata *data) | ||
| 57 | { | ||
| 58 | kfree(data->args.name.name); | ||
| 59 | data->args.name.name = NULL; | ||
| 60 | data->args.name.len = 0; | ||
| 61 | } | ||
| 62 | |||
| 63 | static void nfs_dec_sillycount(struct inode *dir) | ||
| 64 | { | ||
| 65 | struct nfs_inode *nfsi = NFS_I(dir); | ||
| 66 | if (atomic_dec_return(&nfsi->silly_count) == 1) | ||
| 67 | wake_up(&nfsi->waitqueue); | ||
| 68 | } | ||
| 69 | |||
| 55 | /** | 70 | /** |
| 56 | * nfs_async_unlink_init - Initialize the RPC info | 71 | * nfs_async_unlink_init - Initialize the RPC info |
| 57 | * task: rpc_task of the sillydelete | 72 | * task: rpc_task of the sillydelete |
| @@ -95,6 +110,8 @@ static void nfs_async_unlink_done(struct rpc_task *task, void *calldata) | |||
| 95 | static void nfs_async_unlink_release(void *calldata) | 110 | static void nfs_async_unlink_release(void *calldata) |
| 96 | { | 111 | { |
| 97 | struct nfs_unlinkdata *data = calldata; | 112 | struct nfs_unlinkdata *data = calldata; |
| 113 | |||
| 114 | nfs_dec_sillycount(data->dir); | ||
| 98 | nfs_free_unlinkdata(data); | 115 | nfs_free_unlinkdata(data); |
| 99 | } | 116 | } |
| 100 | 117 | ||
| @@ -104,33 +121,100 @@ static const struct rpc_call_ops nfs_unlink_ops = { | |||
| 104 | .rpc_release = nfs_async_unlink_release, | 121 | .rpc_release = nfs_async_unlink_release, |
| 105 | }; | 122 | }; |
| 106 | 123 | ||
| 107 | static int nfs_call_unlink(struct dentry *dentry, struct nfs_unlinkdata *data) | 124 | static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct nfs_unlinkdata *data) |
| 108 | { | 125 | { |
| 109 | struct rpc_task *task; | 126 | struct rpc_task *task; |
| 127 | struct dentry *alias; | ||
| 128 | |||
| 129 | alias = d_lookup(parent, &data->args.name); | ||
| 130 | if (alias != NULL) { | ||
| 131 | int ret = 0; | ||
| 132 | /* | ||
| 133 | * Hey, we raced with lookup... See if we need to transfer | ||
| 134 | * the sillyrename information to the aliased dentry. | ||
| 135 | */ | ||
| 136 | nfs_free_dname(data); | ||
| 137 | spin_lock(&alias->d_lock); | ||
| 138 | if (!(alias->d_flags & DCACHE_NFSFS_RENAMED)) { | ||
| 139 | alias->d_fsdata = data; | ||
| 140 | alias->d_flags ^= DCACHE_NFSFS_RENAMED; | ||
| 141 | ret = 1; | ||
| 142 | } | ||
| 143 | spin_unlock(&alias->d_lock); | ||
| 144 | nfs_dec_sillycount(dir); | ||
| 145 | dput(alias); | ||
| 146 | return ret; | ||
| 147 | } | ||
| 148 | data->dir = igrab(dir); | ||
| 149 | if (!data->dir) { | ||
| 150 | nfs_dec_sillycount(dir); | ||
| 151 | return 0; | ||
| 152 | } | ||
| 153 | data->args.fh = NFS_FH(dir); | ||
| 154 | nfs_fattr_init(&data->res.dir_attr); | ||
| 155 | |||
| 156 | task = rpc_run_task(NFS_CLIENT(dir), RPC_TASK_ASYNC, &nfs_unlink_ops, data); | ||
| 157 | if (!IS_ERR(task)) | ||
| 158 | rpc_put_task(task); | ||
| 159 | return 1; | ||
| 160 | } | ||
| 161 | |||
| 162 | static int nfs_call_unlink(struct dentry *dentry, struct nfs_unlinkdata *data) | ||
| 163 | { | ||
| 110 | struct dentry *parent; | 164 | struct dentry *parent; |
| 111 | struct inode *dir; | 165 | struct inode *dir; |
| 166 | int ret = 0; | ||
| 112 | 167 | ||
| 113 | if (nfs_copy_dname(dentry, data) < 0) | ||
| 114 | goto out_free; | ||
| 115 | 168 | ||
| 116 | parent = dget_parent(dentry); | 169 | parent = dget_parent(dentry); |
| 117 | if (parent == NULL) | 170 | if (parent == NULL) |
| 118 | goto out_free; | 171 | goto out_free; |
| 119 | dir = igrab(parent->d_inode); | 172 | dir = parent->d_inode; |
| 173 | if (nfs_copy_dname(dentry, data) == 0) | ||
| 174 | goto out_dput; | ||
| 175 | /* Non-exclusive lock protects against concurrent lookup() calls */ | ||
| 176 | spin_lock(&dir->i_lock); | ||
| 177 | if (atomic_inc_not_zero(&NFS_I(dir)->silly_count) == 0) { | ||
| 178 | /* Deferred delete */ | ||
| 179 | hlist_add_head(&data->list, &NFS_I(dir)->silly_list); | ||
| 180 | spin_unlock(&dir->i_lock); | ||
| 181 | ret = 1; | ||
| 182 | goto out_dput; | ||
| 183 | } | ||
| 184 | spin_unlock(&dir->i_lock); | ||
| 185 | ret = nfs_do_call_unlink(parent, dir, data); | ||
| 186 | out_dput: | ||
| 120 | dput(parent); | 187 | dput(parent); |
| 121 | if (dir == NULL) | 188 | out_free: |
| 122 | goto out_free; | 189 | return ret; |
| 190 | } | ||
| 123 | 191 | ||
| 124 | data->dir = dir; | 192 | void nfs_block_sillyrename(struct dentry *dentry) |
| 125 | data->args.fh = NFS_FH(dir); | 193 | { |
| 126 | nfs_fattr_init(&data->res.dir_attr); | 194 | struct nfs_inode *nfsi = NFS_I(dentry->d_inode); |
| 127 | 195 | ||
| 128 | task = rpc_run_task(NFS_CLIENT(dir), RPC_TASK_ASYNC, &nfs_unlink_ops, data); | 196 | wait_event(nfsi->waitqueue, atomic_cmpxchg(&nfsi->silly_count, 1, 0) == 1); |
| 129 | if (!IS_ERR(task)) | 197 | } |
| 130 | rpc_put_task(task); | 198 | |
| 131 | return 1; | 199 | void nfs_unblock_sillyrename(struct dentry *dentry) |
| 132 | out_free: | 200 | { |
| 133 | return 0; | 201 | struct inode *dir = dentry->d_inode; |
| 202 | struct nfs_inode *nfsi = NFS_I(dir); | ||
| 203 | struct nfs_unlinkdata *data; | ||
| 204 | |||
| 205 | atomic_inc(&nfsi->silly_count); | ||
| 206 | spin_lock(&dir->i_lock); | ||
| 207 | while (!hlist_empty(&nfsi->silly_list)) { | ||
| 208 | if (!atomic_inc_not_zero(&nfsi->silly_count)) | ||
| 209 | break; | ||
| 210 | data = hlist_entry(nfsi->silly_list.first, struct nfs_unlinkdata, list); | ||
| 211 | hlist_del(&data->list); | ||
| 212 | spin_unlock(&dir->i_lock); | ||
| 213 | if (nfs_do_call_unlink(dentry, dir, data) == 0) | ||
| 214 | nfs_free_unlinkdata(data); | ||
| 215 | spin_lock(&dir->i_lock); | ||
| 216 | } | ||
| 217 | spin_unlock(&dir->i_lock); | ||
| 134 | } | 218 | } |
| 135 | 219 | ||
| 136 | /** | 220 | /** |
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index c5164c257f71..e82a6ebc725d 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h | |||
| @@ -160,6 +160,12 @@ struct nfs_inode { | |||
| 160 | /* Open contexts for shared mmap writes */ | 160 | /* Open contexts for shared mmap writes */ |
| 161 | struct list_head open_files; | 161 | struct list_head open_files; |
| 162 | 162 | ||
| 163 | /* Number of in-flight sillydelete RPC calls */ | ||
| 164 | atomic_t silly_count; | ||
| 165 | /* List of deferred sillydelete requests */ | ||
| 166 | struct hlist_head silly_list; | ||
| 167 | wait_queue_head_t waitqueue; | ||
| 168 | |||
| 163 | #ifdef CONFIG_NFS_V4 | 169 | #ifdef CONFIG_NFS_V4 |
| 164 | struct nfs4_cached_acl *nfs4_acl; | 170 | struct nfs4_cached_acl *nfs4_acl; |
| 165 | /* NFSv4 state */ | 171 | /* NFSv4 state */ |
| @@ -394,6 +400,8 @@ extern void nfs_release_automount_timer(void); | |||
| 394 | */ | 400 | */ |
| 395 | extern int nfs_async_unlink(struct inode *dir, struct dentry *dentry); | 401 | extern int nfs_async_unlink(struct inode *dir, struct dentry *dentry); |
| 396 | extern void nfs_complete_unlink(struct dentry *dentry, struct inode *); | 402 | extern void nfs_complete_unlink(struct dentry *dentry, struct inode *); |
| 403 | extern void nfs_block_sillyrename(struct dentry *dentry); | ||
| 404 | extern void nfs_unblock_sillyrename(struct dentry *dentry); | ||
| 397 | 405 | ||
| 398 | /* | 406 | /* |
| 399 | * linux/fs/nfs/write.c | 407 | * linux/fs/nfs/write.c |
