diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2007-09-25 15:55:03 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-09-26 12:22:04 -0400 |
commit | 255129d1e9ca0ed3d69d5517fae3e03d7ab4b806 (patch) | |
tree | e3dedbffebd4f0e15da97684500296c9bc4d3d6a | |
parent | f6a592e8abd2f2394623ce5427cbb4c265495974 (diff) |
NLM: Fix a circular lock dependency in lockd
The problem is that the garbage collector for the 'host' structures
nlm_gc_hosts(), holds nlm_host_mutex while calling down to
nlmsvc_mark_resources, which, eventually takes the file->f_mutex.
We cannot therefore call nlmsvc_lookup_host() from within
nlmsvc_create_block, since the caller will already hold file->f_mutex, so
the attempt to grab nlm_host_mutex may deadlock.
Fix the problem by calling nlmsvc_lookup_host() outside the file->f_mutex.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/lockd/svclock.c | 29 |
1 files changed, 18 insertions, 11 deletions
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index a21e4bc5444b..d098c7af0d22 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c | |||
@@ -171,19 +171,14 @@ found: | |||
171 | * GRANTED_RES message by cookie, without having to rely on the client's IP | 171 | * GRANTED_RES message by cookie, without having to rely on the client's IP |
172 | * address. --okir | 172 | * address. --okir |
173 | */ | 173 | */ |
174 | static inline struct nlm_block * | 174 | static struct nlm_block * |
175 | nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, | 175 | nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_host *host, |
176 | struct nlm_lock *lock, struct nlm_cookie *cookie) | 176 | struct nlm_file *file, struct nlm_lock *lock, |
177 | struct nlm_cookie *cookie) | ||
177 | { | 178 | { |
178 | struct nlm_block *block; | 179 | struct nlm_block *block; |
179 | struct nlm_host *host; | ||
180 | struct nlm_rqst *call = NULL; | 180 | struct nlm_rqst *call = NULL; |
181 | 181 | ||
182 | /* Create host handle for callback */ | ||
183 | host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len); | ||
184 | if (host == NULL) | ||
185 | return NULL; | ||
186 | |||
187 | call = nlm_alloc_call(host); | 182 | call = nlm_alloc_call(host); |
188 | if (call == NULL) | 183 | if (call == NULL) |
189 | return NULL; | 184 | return NULL; |
@@ -366,6 +361,7 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, | |||
366 | struct nlm_lock *lock, int wait, struct nlm_cookie *cookie) | 361 | struct nlm_lock *lock, int wait, struct nlm_cookie *cookie) |
367 | { | 362 | { |
368 | struct nlm_block *block = NULL; | 363 | struct nlm_block *block = NULL; |
364 | struct nlm_host *host; | ||
369 | int error; | 365 | int error; |
370 | __be32 ret; | 366 | __be32 ret; |
371 | 367 | ||
@@ -377,6 +373,10 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, | |||
377 | (long long)lock->fl.fl_end, | 373 | (long long)lock->fl.fl_end, |
378 | wait); | 374 | wait); |
379 | 375 | ||
376 | /* Create host handle for callback */ | ||
377 | host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len); | ||
378 | if (host == NULL) | ||
379 | return nlm_lck_denied_nolocks; | ||
380 | 380 | ||
381 | /* Lock file against concurrent access */ | 381 | /* Lock file against concurrent access */ |
382 | mutex_lock(&file->f_mutex); | 382 | mutex_lock(&file->f_mutex); |
@@ -385,7 +385,8 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, | |||
385 | */ | 385 | */ |
386 | block = nlmsvc_lookup_block(file, lock); | 386 | block = nlmsvc_lookup_block(file, lock); |
387 | if (block == NULL) { | 387 | if (block == NULL) { |
388 | block = nlmsvc_create_block(rqstp, file, lock, cookie); | 388 | block = nlmsvc_create_block(rqstp, nlm_get_host(host), file, |
389 | lock, cookie); | ||
389 | ret = nlm_lck_denied_nolocks; | 390 | ret = nlm_lck_denied_nolocks; |
390 | if (block == NULL) | 391 | if (block == NULL) |
391 | goto out; | 392 | goto out; |
@@ -449,6 +450,7 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, | |||
449 | out: | 450 | out: |
450 | mutex_unlock(&file->f_mutex); | 451 | mutex_unlock(&file->f_mutex); |
451 | nlmsvc_release_block(block); | 452 | nlmsvc_release_block(block); |
453 | nlm_release_host(host); | ||
452 | dprintk("lockd: nlmsvc_lock returned %u\n", ret); | 454 | dprintk("lockd: nlmsvc_lock returned %u\n", ret); |
453 | return ret; | 455 | return ret; |
454 | } | 456 | } |
@@ -477,10 +479,15 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, | |||
477 | 479 | ||
478 | if (block == NULL) { | 480 | if (block == NULL) { |
479 | struct file_lock *conf = kzalloc(sizeof(*conf), GFP_KERNEL); | 481 | struct file_lock *conf = kzalloc(sizeof(*conf), GFP_KERNEL); |
482 | struct nlm_host *host; | ||
480 | 483 | ||
481 | if (conf == NULL) | 484 | if (conf == NULL) |
482 | return nlm_granted; | 485 | return nlm_granted; |
483 | block = nlmsvc_create_block(rqstp, file, lock, cookie); | 486 | /* Create host handle for callback */ |
487 | host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len); | ||
488 | if (host == NULL) | ||
489 | return nlm_lck_denied_nolocks; | ||
490 | block = nlmsvc_create_block(rqstp, host, file, lock, cookie); | ||
484 | if (block == NULL) { | 491 | if (block == NULL) { |
485 | kfree(conf); | 492 | kfree(conf); |
486 | return nlm_granted; | 493 | return nlm_granted; |