diff options
Diffstat (limited to 'fs/nfs/unlink.c')
-rw-r--r-- | fs/nfs/unlink.c | 117 |
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 | ||
16 | struct nfs_unlinkdata { | 17 | struct 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 | ||
57 | static 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 | |||
64 | static 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) | |||
95 | static void nfs_async_unlink_release(void *calldata) | 111 | static 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 | ||
107 | static int nfs_call_unlink(struct dentry *dentry, struct nfs_unlinkdata *data) | 125 | static 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 | |||
163 | static 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); | ||
187 | out_dput: | ||
120 | dput(parent); | 188 | dput(parent); |
121 | if (dir == NULL) | 189 | out_free: |
122 | goto out_free; | 190 | return ret; |
191 | } | ||
123 | 192 | ||
124 | data->dir = dir; | 193 | void 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; | 200 | void nfs_unblock_sillyrename(struct dentry *dentry) |
132 | out_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 | /** |