aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/nfs/dir.c14
-rw-r--r--fs/nfs/inode.c3
-rw-r--r--fs/nfs/nfs4proc.c6
-rw-r--r--fs/nfs/unlink.c114
-rw-r--r--include/linux/nfs_fs.h8
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 = {
866static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) 868static 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
909no_entry: 915no_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));
923out_unblock_sillyrename:
924 nfs_unblock_sillyrename(parent);
917out_unlock: 925out_unlock:
918 unlock_kernel(); 926 unlock_kernel();
919out: 927out:
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:
1372struct dentry * 1372struct dentry *
1373nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd) 1373nfs4_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
16struct nfs_unlinkdata { 16struct 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
56static 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
63static 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)
95static void nfs_async_unlink_release(void *calldata) 110static 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
107static int nfs_call_unlink(struct dentry *dentry, struct nfs_unlinkdata *data) 124static 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
162static 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);
186out_dput:
120 dput(parent); 187 dput(parent);
121 if (dir == NULL) 188out_free:
122 goto out_free; 189 return ret;
190}
123 191
124 data->dir = dir; 192void 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; 199void nfs_unblock_sillyrename(struct dentry *dentry)
132out_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 */
395extern int nfs_async_unlink(struct inode *dir, struct dentry *dentry); 401extern int nfs_async_unlink(struct inode *dir, struct dentry *dentry);
396extern void nfs_complete_unlink(struct dentry *dentry, struct inode *); 402extern void nfs_complete_unlink(struct dentry *dentry, struct inode *);
403extern void nfs_block_sillyrename(struct dentry *dentry);
404extern void nfs_unblock_sillyrename(struct dentry *dentry);
397 405
398/* 406/*
399 * linux/fs/nfs/write.c 407 * linux/fs/nfs/write.c