From: David Teigland <teigland@redhat.com> Subject: [RHEL5.1 PATCH] dlm: fix user unlocking Date: Thu, 25 Jan 2007 09:13:06 -0600 Bugzilla: 219388 Message-Id: <20070125151306.GA21980@redhat.com> Changelog: [dlm] fix user unlocking bz 219388 When a process holding locks exits, we clear all the locks it holds by sending unlocks for all of the remote ones. We aren't properly tracking all these unlocks, so lkb's can either be leaked or we can get an oops if there were in-flight unlocks when the process exited. patch is in upstream gfs2 git tree Index: linux-rhel5-quilt/fs/dlm/dlm_internal.h =================================================================== --- linux-rhel5-quilt.orig/fs/dlm/dlm_internal.h 2007-01-24 14:42:13.000000000 -0600 +++ linux-rhel5-quilt/fs/dlm/dlm_internal.h 2007-01-24 14:57:10.000000000 -0600 @@ -524,6 +524,7 @@ spinlock_t asts_spin; struct list_head locks; spinlock_t locks_spin; + struct list_head unlocking; wait_queue_head_t wait; }; Index: linux-rhel5-quilt/fs/dlm/lock.c =================================================================== --- linux-rhel5-quilt.orig/fs/dlm/lock.c 2007-01-24 14:42:12.000000000 -0600 +++ linux-rhel5-quilt/fs/dlm/lock.c 2007-01-24 14:57:10.000000000 -0600 @@ -3772,12 +3772,10 @@ goto out_put; spin_lock(&ua->proc->locks_spin); - list_del_init(&lkb->lkb_ownqueue); + /* dlm_user_add_ast() may have already taken lkb off the proc list */ + if (!list_empty(&lkb->lkb_ownqueue)) + list_move(&lkb->lkb_ownqueue, &ua->proc->unlocking); spin_unlock(&ua->proc->locks_spin); - - /* this removes the reference for the proc->locks list added by - dlm_user_request */ - unhold_lkb(lkb); out_put: dlm_put_lkb(lkb); out: @@ -3817,9 +3815,8 @@ /* this lkb was removed from the WAITING queue */ if (lkb->lkb_grmode == DLM_LOCK_IV) { spin_lock(&ua->proc->locks_spin); - list_del_init(&lkb->lkb_ownqueue); + list_move(&lkb->lkb_ownqueue, &ua->proc->unlocking); spin_unlock(&ua->proc->locks_spin); - unhold_lkb(lkb); } out_put: dlm_put_lkb(lkb); @@ -3880,11 +3877,6 @@ mutex_lock(&ls->ls_clear_proc_locks); list_for_each_entry_safe(lkb, safe, &proc->locks, lkb_ownqueue) { - if (lkb->lkb_ast_type) { - list_del(&lkb->lkb_astqueue); - unhold_lkb(lkb); - } - list_del_init(&lkb->lkb_ownqueue); if (lkb->lkb_exflags & DLM_LKF_PERSISTENT) { @@ -3901,6 +3893,20 @@ dlm_put_lkb(lkb); } + + /* in-progress unlocks */ + list_for_each_entry_safe(lkb, safe, &proc->unlocking, lkb_ownqueue) { + list_del_init(&lkb->lkb_ownqueue); + lkb->lkb_flags |= DLM_IFL_DEAD; + dlm_put_lkb(lkb); + } + + list_for_each_entry_safe(lkb, safe, &proc->asts, lkb_astqueue) { + list_del(&lkb->lkb_astqueue); + dlm_put_lkb(lkb); + } + mutex_unlock(&ls->ls_clear_proc_locks); unlock_recovery(ls); } + Index: linux-rhel5-quilt/fs/dlm/user.c =================================================================== --- linux-rhel5-quilt.orig/fs/dlm/user.c 2007-01-24 14:42:04.000000000 -0600 +++ linux-rhel5-quilt/fs/dlm/user.c 2007-01-24 14:57:10.000000000 -0600 @@ -180,6 +180,14 @@ ua->lksb.sb_status == -EAGAIN && !list_empty(&lkb->lkb_ownqueue)) remove_ownqueue = 1; + /* unlocks or cancels of waiting requests need to be removed from the + proc's unlocking list, again there must be a better way... */ + + if (ua->lksb.sb_status == -DLM_EUNLOCK || + (ua->lksb.sb_status == -DLM_ECANCEL && + lkb->lkb_grmode == DLM_LOCK_IV)) + remove_ownqueue = 1; + /* We want to copy the lvb to userspace when the completion ast is read if the status is 0, the lock has an lvb and lvb_ops says we should. We could probably have set_lvb_lock() @@ -523,6 +531,7 @@ proc->lockspace = ls->ls_local_handle; INIT_LIST_HEAD(&proc->asts); INIT_LIST_HEAD(&proc->locks); + INIT_LIST_HEAD(&proc->unlocking); spin_lock_init(&proc->asts_spin); spin_lock_init(&proc->locks_spin); init_waitqueue_head(&proc->wait);