From: David Teigland Fix a race where an attempt to unlock a lock in the completion AST routine could crash on SMP. Signed-off-by: Patrick Caulfield Signed-off-by: David Teigland Signed-off-by: Andrew Morton --- drivers/dlm/device.c | 11 +++++++++++ 1 files changed, 11 insertions(+) diff -puN drivers/dlm/device.c~dlm-device-interface-fix-unlock-race drivers/dlm/device.c --- 25/drivers/dlm/device.c~dlm-device-interface-fix-unlock-race Thu Jan 19 16:00:26 2006 +++ 25-akpm/drivers/dlm/device.c Thu Jan 19 16:00:26 2006 @@ -54,6 +54,7 @@ static rwlock_t lockinfo_lock; #define LI_FLAG_COMPLETE 1 #define LI_FLAG_FIRSTLOCK 2 #define LI_FLAG_PERSISTENT 3 +#define LI_FLAG_ONLIST 4 /* flags in ls_flags*/ #define LS_FLAG_DELETED 1 @@ -383,6 +384,7 @@ static void ast_routine(void *param) spin_lock(&li->li_file->fi_li_lock); list_del(&li->li_ownerqueue); + clear_bit(LI_FLAG_ONLIST, &li->li_flags); spin_unlock(&li->li_file->fi_li_lock); release_lockinfo(li); return; @@ -890,6 +892,7 @@ static int do_user_lock(struct file_info spin_lock(&fi->fi_li_lock); list_add(&li->li_ownerqueue, &fi->fi_li_list); + set_bit(LI_FLAG_ONLIST, &li->li_flags); spin_unlock(&fi->fi_li_lock); if (add_lockinfo(li)) printk(KERN_WARNING "Add lockinfo failed\n"); @@ -921,6 +924,7 @@ static int do_user_unlock(struct file_in return -ENOMEM; spin_lock(&fi->fi_li_lock); list_add(&li->li_ownerqueue, &fi->fi_li_list); + set_bit(LI_FLAG_ONLIST, &li->li_flags); spin_unlock(&fi->fi_li_lock); } @@ -935,6 +939,12 @@ static int do_user_unlock(struct file_in if (kparams->flags & DLM_LKF_CANCEL && li->li_grmode != -1) convert_cancel = 1; + /* Wait until dlm_lock() has completed */ + if (!test_bit(LI_FLAG_ONLIST, &li->li_flags)) { + down(&li->li_firstlock); + up(&li->li_firstlock); + } + /* dlm_unlock() passes a 0 for castaddr which means don't overwrite the existing li_castaddr as that's the completion routine for unlocks. dlm_unlock_wait() specifies a new AST routine to be @@ -950,6 +960,7 @@ static int do_user_unlock(struct file_in if (!status && !convert_cancel) { spin_lock(&fi->fi_li_lock); list_del(&li->li_ownerqueue); + clear_bit(LI_FLAG_ONLIST, &li->li_flags); spin_unlock(&fi->fi_li_lock); } _