aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/unlink.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfs/unlink.c')
-rw-r--r--fs/nfs/unlink.c114
1 files changed, 99 insertions, 15 deletions
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/**