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 |