diff options
Diffstat (limited to 'fs/lockd/svcsubs.c')
-rw-r--r-- | fs/lockd/svcsubs.c | 105 |
1 files changed, 77 insertions, 28 deletions
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); |