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.c117
1 files changed, 101 insertions, 16 deletions
diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c
index 1aed850d18f2..ce558c2e4d53 100644
--- a/fs/nfs/unlink.c
+++ b/fs/nfs/unlink.c
@@ -11,9 +11,11 @@
11#include <linux/sunrpc/sched.h> 11#include <linux/sunrpc/sched.h>
12#include <linux/sunrpc/clnt.h> 12#include <linux/sunrpc/clnt.h>
13#include <linux/nfs_fs.h> 13#include <linux/nfs_fs.h>
14 14#include <linux/sched.h>
15#include <linux/wait.h>
15 16
16struct nfs_unlinkdata { 17struct nfs_unlinkdata {
18 struct hlist_node list;
17 struct nfs_removeargs args; 19 struct nfs_removeargs args;
18 struct nfs_removeres res; 20 struct nfs_removeres res;
19 struct inode *dir; 21 struct inode *dir;
@@ -52,6 +54,20 @@ static int nfs_copy_dname(struct dentry *dentry, struct nfs_unlinkdata *data)
52 return 0; 54 return 0;
53} 55}
54 56
57static void nfs_free_dname(struct nfs_unlinkdata *data)
58{
59 kfree(data->args.name.name);
60 data->args.name.name = NULL;
61 data->args.name.len = 0;
62}
63
64static void nfs_dec_sillycount(struct inode *dir)
65{
66 struct nfs_inode *nfsi = NFS_I(dir);
67 if (atomic_dec_return(&nfsi->silly_count) == 1)
68 wake_up(&nfsi->waitqueue);
69}
70
55/** 71/**
56 * nfs_async_unlink_init - Initialize the RPC info 72 * nfs_async_unlink_init - Initialize the RPC info
57 * task: rpc_task of the sillydelete 73 * task: rpc_task of the sillydelete
@@ -95,6 +111,8 @@ static void nfs_async_unlink_done(struct rpc_task *task, void *calldata)
95static void nfs_async_unlink_release(void *calldata) 111static void nfs_async_unlink_release(void *calldata)
96{ 112{
97 struct nfs_unlinkdata *data = calldata; 113 struct nfs_unlinkdata *data = calldata;
114
115 nfs_dec_sillycount(data->dir);
98 nfs_free_unlinkdata(data); 116 nfs_free_unlinkdata(data);
99} 117}
100 118
@@ -104,33 +122,100 @@ static const struct rpc_call_ops nfs_unlink_ops = {
104 .rpc_release = nfs_async_unlink_release, 122 .rpc_release = nfs_async_unlink_release,
105}; 123};
106 124
107static int nfs_call_unlink(struct dentry *dentry, struct nfs_unlinkdata *data) 125static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct nfs_unlinkdata *data)
108{ 126{
109 struct rpc_task *task; 127 struct rpc_task *task;
128 struct dentry *alias;
129
130 alias = d_lookup(parent, &data->args.name);
131 if (alias != NULL) {
132 int ret = 0;
133 /*
134 * Hey, we raced with lookup... See if we need to transfer
135 * the sillyrename information to the aliased dentry.
136 */
137 nfs_free_dname(data);
138 spin_lock(&alias->d_lock);
139 if (!(alias->d_flags & DCACHE_NFSFS_RENAMED)) {
140 alias->d_fsdata = data;
141 alias->d_flags ^= DCACHE_NFSFS_RENAMED;
142 ret = 1;
143 }
144 spin_unlock(&alias->d_lock);
145 nfs_dec_sillycount(dir);
146 dput(alias);
147 return ret;
148 }
149 data->dir = igrab(dir);
150 if (!data->dir) {
151 nfs_dec_sillycount(dir);
152 return 0;
153 }
154 data->args.fh = NFS_FH(dir);
155 nfs_fattr_init(&data->res.dir_attr);
156
157 task = rpc_run_task(NFS_CLIENT(dir), RPC_TASK_ASYNC, &nfs_unlink_ops, data);
158 if (!IS_ERR(task))
159 rpc_put_task(task);
160 return 1;
161}
162
163static int nfs_call_unlink(struct dentry *dentry, struct nfs_unlinkdata *data)
164{
110 struct dentry *parent; 165 struct dentry *parent;
111 struct inode *dir; 166 struct inode *dir;
167 int ret = 0;
112 168
113 if (nfs_copy_dname(dentry, data) < 0)
114 goto out_free;
115 169
116 parent = dget_parent(dentry); 170 parent = dget_parent(dentry);
117 if (parent == NULL) 171 if (parent == NULL)
118 goto out_free; 172 goto out_free;
119 dir = igrab(parent->d_inode); 173 dir = parent->d_inode;
174 if (nfs_copy_dname(dentry, data) == 0)
175 goto out_dput;
176 /* Non-exclusive lock protects against concurrent lookup() calls */
177 spin_lock(&dir->i_lock);
178 if (atomic_inc_not_zero(&NFS_I(dir)->silly_count) == 0) {
179 /* Deferred delete */
180 hlist_add_head(&data->list, &NFS_I(dir)->silly_list);
181 spin_unlock(&dir->i_lock);
182 ret = 1;
183 goto out_dput;
184 }
185 spin_unlock(&dir->i_lock);
186 ret = nfs_do_call_unlink(parent, dir, data);
187out_dput:
120 dput(parent); 188 dput(parent);
121 if (dir == NULL) 189out_free:
122 goto out_free; 190 return ret;
191}
123 192
124 data->dir = dir; 193void nfs_block_sillyrename(struct dentry *dentry)
125 data->args.fh = NFS_FH(dir); 194{
126 nfs_fattr_init(&data->res.dir_attr); 195 struct nfs_inode *nfsi = NFS_I(dentry->d_inode);
127 196
128 task = rpc_run_task(NFS_CLIENT(dir), RPC_TASK_ASYNC, &nfs_unlink_ops, data); 197 wait_event(nfsi->waitqueue, atomic_cmpxchg(&nfsi->silly_count, 1, 0) == 1);
129 if (!IS_ERR(task)) 198}
130 rpc_put_task(task); 199
131 return 1; 200void nfs_unblock_sillyrename(struct dentry *dentry)
132out_free: 201{
133 return 0; 202 struct inode *dir = dentry->d_inode;
203 struct nfs_inode *nfsi = NFS_I(dir);
204 struct nfs_unlinkdata *data;
205
206 atomic_inc(&nfsi->silly_count);
207 spin_lock(&dir->i_lock);
208 while (!hlist_empty(&nfsi->silly_list)) {
209 if (!atomic_inc_not_zero(&nfsi->silly_count))
210 break;
211 data = hlist_entry(nfsi->silly_list.first, struct nfs_unlinkdata, list);
212 hlist_del(&data->list);
213 spin_unlock(&dir->i_lock);
214 if (nfs_do_call_unlink(dentry, dir, data) == 0)
215 nfs_free_unlinkdata(data);
216 spin_lock(&dir->i_lock);
217 }
218 spin_unlock(&dir->i_lock);
134} 219}
135 220
136/** 221/**