We always calling hold_lkb(lkb) if we increment lkb->lkb_wait_count. As
conclusion we always need to call unhold_lkb(lkb) if we decrement
lkb->lkb_wait_count. This patch will add missing unhold_lkb(lkb) if we
decrement lkb->lkb_wait_count. In case of setting lkb->lkb_wait_count to
zero we need to countdown until reaching zero and call unhold_lkb(lkb).
The waiters list unhold_lkb(lkb) can be removed because it's done for
the last lkb_wait_count decrement iteration as it's done in
_remove_from_waiters().

This issue was discovered by a dlm gfs2 test case which use excessively
dlm_unlock(LKF_CANCEL) feature. Probably the lkb->lkb_wait_count value
never reached above 1 if this feature isn't used and so it was not
discovered before.

The testcase ended in a rsb on the rsb keep data structure with a
refcount of 1 but no lkb was associated with it anymore which is itself
an invalid behaviour. As sideeffect it was observed that the dlm
lockspace was in a kind of deadlock state by sending remove messages in
a looping behaviour. With this patch I never observed a similar
behaviour again.

Cc: sta...@vger.kernel.org
Signed-off-by: Alexander Aring <aahri...@redhat.com>
---
 fs/dlm/lock.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/fs/dlm/lock.c b/fs/dlm/lock.c
index 1899bb266e2e..d19cfa03c9bb 100644
--- a/fs/dlm/lock.c
+++ b/fs/dlm/lock.c
@@ -1578,6 +1578,7 @@ static int _remove_from_waiters(struct dlm_lkb *lkb, int 
mstype,
                lkb->lkb_wait_type = 0;
                lkb->lkb_flags &= ~DLM_IFL_OVERLAP_CANCEL;
                lkb->lkb_wait_count--;
+               unhold_lkb(lkb);
                goto out_del;
        }
 
@@ -1604,6 +1605,7 @@ static int _remove_from_waiters(struct dlm_lkb *lkb, int 
mstype,
                log_error(ls, "remwait error %x reply %d wait_type %d overlap",
                          lkb->lkb_id, mstype, lkb->lkb_wait_type);
                lkb->lkb_wait_count--;
+               unhold_lkb(lkb);
                lkb->lkb_wait_type = 0;
        }
 
@@ -5364,11 +5366,16 @@ int dlm_recover_waiters_post(struct dlm_ls *ls)
                lkb->lkb_flags &= ~DLM_IFL_OVERLAP_UNLOCK;
                lkb->lkb_flags &= ~DLM_IFL_OVERLAP_CANCEL;
                lkb->lkb_wait_type = 0;
-               lkb->lkb_wait_count = 0;
+               /* drop all wait_count references we still
+                * hold a reference for this iteration.
+                */
+               while (lkb->lkb_wait_count) {
+                       lkb->lkb_wait_count--;
+                       unhold_lkb(lkb);
+               }
                mutex_lock(&ls->ls_waiters_mutex);
                list_del_init(&lkb->lkb_wait_reply);
                mutex_unlock(&ls->ls_waiters_mutex);
-               unhold_lkb(lkb); /* for waiters list */
 
                if (oc || ou) {
                        /* do an unlock or cancel instead of resending */
-- 
2.31.1

Reply via email to