diff options
Diffstat (limited to 'fs/lockd')
-rw-r--r-- | fs/lockd/svclock.c | 33 | ||||
-rw-r--r-- | fs/lockd/svcshare.c | 20 | ||||
-rw-r--r-- | fs/lockd/svcsubs.c | 105 |
3 files changed, 93 insertions, 65 deletions
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 7209712f3832..1f91567a1b88 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c | |||
@@ -265,24 +265,20 @@ static void nlmsvc_release_block(struct nlm_block *block) | |||
265 | kref_put(&block->b_count, nlmsvc_free_block); | 265 | kref_put(&block->b_count, nlmsvc_free_block); |
266 | } | 266 | } |
267 | 267 | ||
268 | static void nlmsvc_act_mark(struct nlm_host *host, struct nlm_file *file) | 268 | /* |
269 | { | 269 | * Loop over all blocks and delete blocks held by |
270 | struct nlm_block *block; | 270 | * a matching host. |
271 | 271 | */ | |
272 | down(&file->f_sema); | 272 | void nlmsvc_traverse_blocks(struct nlm_host *host, |
273 | list_for_each_entry(block, &file->f_blocks, b_flist) | 273 | struct nlm_file *file, |
274 | block->b_host->h_inuse = 1; | 274 | nlm_host_match_fn_t match) |
275 | up(&file->f_sema); | ||
276 | } | ||
277 | |||
278 | static void nlmsvc_act_unlock(struct nlm_host *host, struct nlm_file *file) | ||
279 | { | 275 | { |
280 | struct nlm_block *block, *next; | 276 | struct nlm_block *block, *next; |
281 | 277 | ||
282 | restart: | 278 | restart: |
283 | down(&file->f_sema); | 279 | down(&file->f_sema); |
284 | list_for_each_entry_safe(block, next, &file->f_blocks, b_flist) { | 280 | list_for_each_entry_safe(block, next, &file->f_blocks, b_flist) { |
285 | if (host != NULL && host != block->b_host) | 281 | if (!match(block->b_host, host)) |
286 | continue; | 282 | continue; |
287 | /* Do not destroy blocks that are not on | 283 | /* Do not destroy blocks that are not on |
288 | * the global retry list - why? */ | 284 | * the global retry list - why? */ |
@@ -298,19 +294,6 @@ restart: | |||
298 | } | 294 | } |
299 | 295 | ||
300 | /* | 296 | /* |
301 | * Loop over all blocks and perform the action specified. | ||
302 | * (NLM_ACT_CHECK handled by nlmsvc_inspect_file). | ||
303 | */ | ||
304 | void | ||
305 | nlmsvc_traverse_blocks(struct nlm_host *host, struct nlm_file *file, int action) | ||
306 | { | ||
307 | if (action == NLM_ACT_MARK) | ||
308 | nlmsvc_act_mark(host, file); | ||
309 | else | ||
310 | nlmsvc_act_unlock(host, file); | ||
311 | } | ||
312 | |||
313 | /* | ||
314 | * Initialize arguments for GRANTED call. The nlm_rqst structure | 297 | * Initialize arguments for GRANTED call. The nlm_rqst structure |
315 | * has been cleared already. | 298 | * has been cleared already. |
316 | */ | 299 | */ |
diff --git a/fs/lockd/svcshare.c b/fs/lockd/svcshare.c index 27288c83da96..b9926ce8782e 100644 --- a/fs/lockd/svcshare.c +++ b/fs/lockd/svcshare.c | |||
@@ -85,24 +85,20 @@ nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file, | |||
85 | } | 85 | } |
86 | 86 | ||
87 | /* | 87 | /* |
88 | * Traverse all shares for a given file (and host). | 88 | * Traverse all shares for a given file, and delete |
89 | * NLM_ACT_CHECK is handled by nlmsvc_inspect_file. | 89 | * those owned by the given (type of) host |
90 | */ | 90 | */ |
91 | void | 91 | void nlmsvc_traverse_shares(struct nlm_host *host, struct nlm_file *file, |
92 | nlmsvc_traverse_shares(struct nlm_host *host, struct nlm_file *file, int action) | 92 | nlm_host_match_fn_t match) |
93 | { | 93 | { |
94 | struct nlm_share *share, **shpp; | 94 | struct nlm_share *share, **shpp; |
95 | 95 | ||
96 | shpp = &file->f_shares; | 96 | shpp = &file->f_shares; |
97 | while ((share = *shpp) != NULL) { | 97 | while ((share = *shpp) != NULL) { |
98 | if (action == NLM_ACT_MARK) | 98 | if (match(share->s_host, host)) { |
99 | share->s_host->h_inuse = 1; | 99 | *shpp = share->s_next; |
100 | else if (action == NLM_ACT_UNLOCK) { | 100 | kfree(share); |
101 | if (host == NULL || host == share->s_host) { | 101 | continue; |
102 | *shpp = share->s_next; | ||
103 | kfree(share); | ||
104 | continue; | ||
105 | } | ||
106 | } | 102 | } |
107 | shpp = &share->s_next; | 103 | shpp = &share->s_next; |
108 | } | 104 | } |
diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 91731353dfa4..bb13a48210f5 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c | |||
@@ -165,7 +165,8 @@ nlm_delete_file(struct nlm_file *file) | |||
165 | * action. | 165 | * action. |
166 | */ | 166 | */ |
167 | static int | 167 | static int |
168 | nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, int action) | 168 | nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, |
169 | nlm_host_match_fn_t match) | ||
169 | { | 170 | { |
170 | struct inode *inode = nlmsvc_file_inode(file); | 171 | struct inode *inode = nlmsvc_file_inode(file); |
171 | struct file_lock *fl; | 172 | struct file_lock *fl; |
@@ -179,17 +180,11 @@ again: | |||
179 | 180 | ||
180 | /* update current lock count */ | 181 | /* update current lock count */ |
181 | file->f_locks++; | 182 | file->f_locks++; |
183 | |||
182 | lockhost = (struct nlm_host *) fl->fl_owner; | 184 | lockhost = (struct nlm_host *) fl->fl_owner; |
183 | if (action == NLM_ACT_MARK) | 185 | if (match(lockhost, host)) { |
184 | lockhost->h_inuse = 1; | ||
185 | else if (action == NLM_ACT_CHECK) | ||
186 | return 1; | ||
187 | else if (action == NLM_ACT_UNLOCK) { | ||
188 | struct file_lock lock = *fl; | 186 | struct file_lock lock = *fl; |
189 | 187 | ||
190 | if (host && lockhost != host) | ||
191 | continue; | ||
192 | |||
193 | lock.fl_type = F_UNLCK; | 188 | lock.fl_type = F_UNLCK; |
194 | lock.fl_start = 0; | 189 | lock.fl_start = 0; |
195 | lock.fl_end = OFFSET_MAX; | 190 | lock.fl_end = OFFSET_MAX; |
@@ -206,27 +201,42 @@ again: | |||
206 | } | 201 | } |
207 | 202 | ||
208 | /* | 203 | /* |
209 | * Operate on a single file | 204 | * Inspect a single file |
205 | */ | ||
206 | static inline int | ||
207 | nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, nlm_host_match_fn_t match) | ||
208 | { | ||
209 | nlmsvc_traverse_blocks(host, file, match); | ||
210 | nlmsvc_traverse_shares(host, file, match); | ||
211 | return nlm_traverse_locks(host, file, match); | ||
212 | } | ||
213 | |||
214 | /* | ||
215 | * Quick check whether there are still any locks, blocks or | ||
216 | * shares on a given file. | ||
210 | */ | 217 | */ |
211 | static inline int | 218 | static inline int |
212 | nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, int action) | 219 | nlm_file_inuse(struct nlm_file *file) |
213 | { | 220 | { |
214 | if (action == NLM_ACT_CHECK) { | 221 | struct inode *inode = nlmsvc_file_inode(file); |
215 | /* Fast path for mark and sweep garbage collection */ | 222 | struct file_lock *fl; |
216 | if (file->f_count || list_empty(&file->f_blocks) || file->f_shares) | 223 | |
224 | if (file->f_count || !list_empty(&file->f_blocks) || file->f_shares) | ||
225 | return 1; | ||
226 | |||
227 | for (fl = inode->i_flock; fl; fl = fl->fl_next) { | ||
228 | if (fl->fl_lmops == &nlmsvc_lock_operations) | ||
217 | return 1; | 229 | return 1; |
218 | } else { | ||
219 | nlmsvc_traverse_blocks(host, file, action); | ||
220 | nlmsvc_traverse_shares(host, file, action); | ||
221 | } | 230 | } |
222 | return nlm_traverse_locks(host, file, action); | 231 | file->f_locks = 0; |
232 | return 0; | ||
223 | } | 233 | } |
224 | 234 | ||
225 | /* | 235 | /* |
226 | * Loop over all files in the file table. | 236 | * Loop over all files in the file table. |
227 | */ | 237 | */ |
228 | static int | 238 | static int |
229 | nlm_traverse_files(struct nlm_host *host, int action) | 239 | nlm_traverse_files(struct nlm_host *host, nlm_host_match_fn_t match) |
230 | { | 240 | { |
231 | struct hlist_node *pos, *next; | 241 | struct hlist_node *pos, *next; |
232 | struct nlm_file *file; | 242 | struct nlm_file *file; |
@@ -240,7 +250,7 @@ nlm_traverse_files(struct nlm_host *host, int action) | |||
240 | 250 | ||
241 | /* Traverse locks, blocks and shares of this file | 251 | /* Traverse locks, blocks and shares of this file |
242 | * and update file->f_locks count */ | 252 | * and update file->f_locks count */ |
243 | if (nlm_inspect_file(host, file, action)) | 253 | if (nlm_inspect_file(host, file, match)) |
244 | ret = 1; | 254 | ret = 1; |
245 | 255 | ||
246 | mutex_lock(&nlm_file_mutex); | 256 | mutex_lock(&nlm_file_mutex); |
@@ -277,23 +287,54 @@ nlm_release_file(struct nlm_file *file) | |||
277 | mutex_lock(&nlm_file_mutex); | 287 | mutex_lock(&nlm_file_mutex); |
278 | 288 | ||
279 | /* If there are no more locks etc, delete the file */ | 289 | /* If there are no more locks etc, delete the file */ |
280 | if(--file->f_count == 0) { | 290 | if (--file->f_count == 0 && !nlm_file_inuse(file)) |
281 | if(!nlm_inspect_file(NULL, file, NLM_ACT_CHECK)) | 291 | nlm_delete_file(file); |
282 | nlm_delete_file(file); | ||
283 | } | ||
284 | 292 | ||
285 | mutex_unlock(&nlm_file_mutex); | 293 | mutex_unlock(&nlm_file_mutex); |
286 | } | 294 | } |
287 | 295 | ||
288 | /* | 296 | /* |
297 | * Helpers function for resource traversal | ||
298 | * | ||
299 | * nlmsvc_mark_host: | ||
300 | * used by the garbage collector; simply sets h_inuse. | ||
301 | * Always returns 0. | ||
302 | * | ||
303 | * nlmsvc_same_host: | ||
304 | * returns 1 iff the two hosts match. Used to release | ||
305 | * all resources bound to a specific host. | ||
306 | * | ||
307 | * nlmsvc_is_client: | ||
308 | * returns 1 iff the host is a client. | ||
309 | * Used by nlmsvc_invalidate_all | ||
310 | */ | ||
311 | static int | ||
312 | nlmsvc_mark_host(struct nlm_host *host, struct nlm_host *dummy) | ||
313 | { | ||
314 | host->h_inuse = 1; | ||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | static int | ||
319 | nlmsvc_same_host(struct nlm_host *host, struct nlm_host *other) | ||
320 | { | ||
321 | return host == other; | ||
322 | } | ||
323 | |||
324 | static int | ||
325 | nlmsvc_is_client(struct nlm_host *host, struct nlm_host *dummy) | ||
326 | { | ||
327 | return host->h_server; | ||
328 | } | ||
329 | |||
330 | /* | ||
289 | * Mark all hosts that still hold resources | 331 | * Mark all hosts that still hold resources |
290 | */ | 332 | */ |
291 | void | 333 | void |
292 | nlmsvc_mark_resources(void) | 334 | nlmsvc_mark_resources(void) |
293 | { | 335 | { |
294 | dprintk("lockd: nlmsvc_mark_resources\n"); | 336 | dprintk("lockd: nlmsvc_mark_resources\n"); |
295 | 337 | nlm_traverse_files(NULL, nlmsvc_mark_host); | |
296 | nlm_traverse_files(NULL, NLM_ACT_MARK); | ||
297 | } | 338 | } |
298 | 339 | ||
299 | /* | 340 | /* |
@@ -304,7 +345,7 @@ nlmsvc_free_host_resources(struct nlm_host *host) | |||
304 | { | 345 | { |
305 | dprintk("lockd: nlmsvc_free_host_resources\n"); | 346 | dprintk("lockd: nlmsvc_free_host_resources\n"); |
306 | 347 | ||
307 | if (nlm_traverse_files(host, NLM_ACT_UNLOCK)) { | 348 | if (nlm_traverse_files(host, nlmsvc_same_host)) { |
308 | printk(KERN_WARNING | 349 | printk(KERN_WARNING |
309 | "lockd: couldn't remove all locks held by %s\n", | 350 | "lockd: couldn't remove all locks held by %s\n", |
310 | host->h_name); | 351 | host->h_name); |
@@ -319,8 +360,16 @@ void | |||
319 | nlmsvc_invalidate_all(void) | 360 | nlmsvc_invalidate_all(void) |
320 | { | 361 | { |
321 | struct nlm_host *host; | 362 | struct nlm_host *host; |
363 | |||
364 | /* Release all locks held by NFS clients. | ||
365 | * Previously, the code would call | ||
366 | * nlmsvc_free_host_resources for each client in | ||
367 | * turn, which is about as inefficient as it gets. | ||
368 | * Now we just do it once in nlm_traverse_files. | ||
369 | */ | ||
370 | nlm_traverse_files(NULL, nlmsvc_is_client); | ||
371 | |||
322 | while ((host = nlm_find_client()) != NULL) { | 372 | while ((host = nlm_find_client()) != NULL) { |
323 | nlmsvc_free_host_resources(host); | ||
324 | host->h_expires = 0; | 373 | host->h_expires = 0; |
325 | host->h_killed = 1; | 374 | host->h_killed = 1; |
326 | nlm_release_host(host); | 375 | nlm_release_host(host); |