diff options
author | David Teigland <teigland@redhat.com> | 2006-05-02 13:34:03 -0400 |
---|---|---|
committer | Steven Whitehouse <swhiteho@redhat.com> | 2006-05-02 13:34:03 -0400 |
commit | 97a35d1e5fab9ff8de27814082b78b2fc9ad94f0 (patch) | |
tree | 551025da17641ccc4df7378da2bb94cedfb2482e /fs/dlm/lock.c | |
parent | d2d7b8a2a756fb520792ca3db3abdeed9214ae5b (diff) |
[DLM] fix grant_after_purge softlockup
In dlm_grant_after_purge() we were holding a hash table read_lock while
calling put_rsb() which potentially removes the rsb from the hash table,
taking the same lock in write. Fix this by flagging rsb's ahead of time
that have been purged. Then iteratively read_lock the hash table, find a
flagged rsb, unlock, process rsb.
Signed-off-by: David Teigland <teigland@redhat.com>
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
Diffstat (limited to 'fs/dlm/lock.c')
-rw-r--r-- | fs/dlm/lock.c | 42 |
1 files changed, 28 insertions, 14 deletions
diff --git a/fs/dlm/lock.c b/fs/dlm/lock.c index 85a0e73ba808..5f6963904107 100644 --- a/fs/dlm/lock.c +++ b/fs/dlm/lock.c | |||
@@ -3278,6 +3278,7 @@ static void purge_queue(struct dlm_rsb *r, struct list_head *queue, | |||
3278 | 3278 | ||
3279 | list_for_each_entry_safe(lkb, safe, queue, lkb_statequeue) { | 3279 | list_for_each_entry_safe(lkb, safe, queue, lkb_statequeue) { |
3280 | if (test(ls, lkb)) { | 3280 | if (test(ls, lkb)) { |
3281 | rsb_set_flag(r, RSB_LOCKS_PURGED); | ||
3281 | del_lkb(r, lkb); | 3282 | del_lkb(r, lkb); |
3282 | /* this put should free the lkb */ | 3283 | /* this put should free the lkb */ |
3283 | if (!dlm_put_lkb(lkb)) | 3284 | if (!dlm_put_lkb(lkb)) |
@@ -3334,27 +3335,40 @@ int dlm_purge_locks(struct dlm_ls *ls) | |||
3334 | return 0; | 3335 | return 0; |
3335 | } | 3336 | } |
3336 | 3337 | ||
3337 | int dlm_grant_after_purge(struct dlm_ls *ls) | 3338 | static struct dlm_rsb *find_purged_rsb(struct dlm_ls *ls, int bucket) |
3339 | { | ||
3340 | struct dlm_rsb *r, *r_ret = NULL; | ||
3341 | |||
3342 | read_lock(&ls->ls_rsbtbl[bucket].lock); | ||
3343 | list_for_each_entry(r, &ls->ls_rsbtbl[bucket].list, res_hashchain) { | ||
3344 | if (!rsb_flag(r, RSB_LOCKS_PURGED)) | ||
3345 | continue; | ||
3346 | hold_rsb(r); | ||
3347 | rsb_clear_flag(r, RSB_LOCKS_PURGED); | ||
3348 | r_ret = r; | ||
3349 | break; | ||
3350 | } | ||
3351 | read_unlock(&ls->ls_rsbtbl[bucket].lock); | ||
3352 | return r_ret; | ||
3353 | } | ||
3354 | |||
3355 | void dlm_grant_after_purge(struct dlm_ls *ls) | ||
3338 | { | 3356 | { |
3339 | struct dlm_rsb *r; | 3357 | struct dlm_rsb *r; |
3340 | int i; | 3358 | int i; |
3341 | 3359 | ||
3342 | for (i = 0; i < ls->ls_rsbtbl_size; i++) { | 3360 | for (i = 0; i < ls->ls_rsbtbl_size; i++) { |
3343 | read_lock(&ls->ls_rsbtbl[i].lock); | 3361 | r = find_purged_rsb(ls, i); |
3344 | list_for_each_entry(r, &ls->ls_rsbtbl[i].list, res_hashchain) { | 3362 | if (!r) |
3345 | hold_rsb(r); | 3363 | continue; |
3346 | lock_rsb(r); | 3364 | lock_rsb(r); |
3347 | if (is_master(r)) { | 3365 | if (is_master(r)) { |
3348 | grant_pending_locks(r); | 3366 | grant_pending_locks(r); |
3349 | confirm_master(r, 0); | 3367 | confirm_master(r, 0); |
3350 | } | ||
3351 | unlock_rsb(r); | ||
3352 | put_rsb(r); | ||
3353 | } | 3368 | } |
3354 | read_unlock(&ls->ls_rsbtbl[i].lock); | 3369 | unlock_rsb(r); |
3370 | put_rsb(r); | ||
3355 | } | 3371 | } |
3356 | |||
3357 | return 0; | ||
3358 | } | 3372 | } |
3359 | 3373 | ||
3360 | static struct dlm_lkb *search_remid_list(struct list_head *head, int nodeid, | 3374 | static struct dlm_lkb *search_remid_list(struct list_head *head, int nodeid, |