提交 c85d65e9 编写于 作者: D David Teigland 提交者: Steven Whitehouse

[DLM] cancel in conversion deadlock [4/6]

When conversion deadlock is detected, cancel the conversion and return
EDEADLK to the application.  This is a new default behavior where before
the dlm would allow the deadlock to exist indefinately.

The DLM_LKF_NODLCKWT flag can now be used in a conversion to prevent the
dlm from performing conversion deadlock detection/cancelation on it.
The DLM_LKF_CONVDEADLK flag can continue to be used as before to tell the
dlm to demote the granted mode of the lock being converted if it gets into
a conversion deadlock.
Signed-off-by: NDavid Teigland <teigland@redhat.com>
Signed-off-by: NSteven Whitehouse <swhiteho@redhat.com>
上级 d7db923e
...@@ -1408,10 +1408,8 @@ static int queue_conflict(struct list_head *head, struct dlm_lkb *lkb) ...@@ -1408,10 +1408,8 @@ static int queue_conflict(struct list_head *head, struct dlm_lkb *lkb)
* queue for one resource. The granted mode of each lock blocks the requested * queue for one resource. The granted mode of each lock blocks the requested
* mode of the other lock." * mode of the other lock."
* *
* Part 2: if the granted mode of lkb is preventing the first lkb in the * Part 2: if the granted mode of lkb is preventing an earlier lkb in the
* convert queue from being granted, then demote lkb (set grmode to NL). * convert queue from being granted, then deadlk/demote lkb.
* This second form requires that we check for conv-deadlk even when
* now == 0 in _can_be_granted().
* *
* Example: * Example:
* Granted Queue: empty * Granted Queue: empty
...@@ -1420,41 +1418,52 @@ static int queue_conflict(struct list_head *head, struct dlm_lkb *lkb) ...@@ -1420,41 +1418,52 @@ static int queue_conflict(struct list_head *head, struct dlm_lkb *lkb)
* *
* The first lock can't be granted because of the granted mode of the second * The first lock can't be granted because of the granted mode of the second
* lock and the second lock can't be granted because it's not first in the * lock and the second lock can't be granted because it's not first in the
* list. We demote the granted mode of the second lock (the lkb passed to this * list. We either cancel lkb's conversion (PR->EX) and return EDEADLK, or we
* function). * demote the granted mode of lkb (from PR to NL) if it has the CONVDEADLK
* flag set and return DEMOTED in the lksb flags.
* *
* After the resolution, the "grant pending" function needs to go back and try * Originally, this function detected conv-deadlk in a more limited scope:
* to grant locks on the convert queue again since the first lock can now be * - if !modes_compat(lkb1, lkb2) && !modes_compat(lkb2, lkb1), or
* granted. * - if lkb1 was the first entry in the queue (not just earlier), and was
* blocked by the granted mode of lkb2, and there was nothing on the
* granted queue preventing lkb1 from being granted immediately, i.e.
* lkb2 was the only thing preventing lkb1 from being granted.
*
* That second condition meant we'd only say there was conv-deadlk if
* resolving it (by demotion) would lead to the first lock on the convert
* queue being granted right away. It allowed conversion deadlocks to exist
* between locks on the convert queue while they couldn't be granted anyway.
*
* Now, we detect and take action on conversion deadlocks immediately when
* they're created, even if they may not be immediately consequential. If
* lkb1 exists anywhere in the convert queue and lkb2 comes in with a granted
* mode that would prevent lkb1's conversion from being granted, we do a
* deadlk/demote on lkb2 right away and don't let it onto the convert queue.
* I think this means that the lkb_is_ahead condition below should always
* be zero, i.e. there will never be conv-deadlk between two locks that are
* both already on the convert queue.
*/ */
static int conversion_deadlock_detect(struct dlm_rsb *rsb, struct dlm_lkb *lkb) static int conversion_deadlock_detect(struct dlm_rsb *r, struct dlm_lkb *lkb2)
{ {
struct dlm_lkb *this, *first = NULL, *self = NULL; struct dlm_lkb *lkb1;
int lkb_is_ahead = 0;
list_for_each_entry(this, &rsb->res_convertqueue, lkb_statequeue) { list_for_each_entry(lkb1, &r->res_convertqueue, lkb_statequeue) {
if (!first) if (lkb1 == lkb2) {
first = this; lkb_is_ahead = 1;
if (this == lkb) {
self = lkb;
continue; continue;
} }
if (!modes_compat(this, lkb) && !modes_compat(lkb, this)) if (!lkb_is_ahead) {
return 1; if (!modes_compat(lkb2, lkb1))
} return 1;
} else {
/* if lkb is on the convert queue and is preventing the first if (!modes_compat(lkb2, lkb1) &&
from being granted, then there's deadlock and we demote lkb. !modes_compat(lkb1, lkb2))
multiple converting locks may need to do this before the first return 1;
converting lock can be granted. */ }
if (self && self != first) {
if (!modes_compat(lkb, first) &&
!queue_conflict(&rsb->res_grantqueue, first))
return 1;
} }
return 0; return 0;
} }
...@@ -1583,42 +1592,57 @@ static int _can_be_granted(struct dlm_rsb *r, struct dlm_lkb *lkb, int now) ...@@ -1583,42 +1592,57 @@ static int _can_be_granted(struct dlm_rsb *r, struct dlm_lkb *lkb, int now)
if (!now && !conv && list_empty(&r->res_convertqueue) && if (!now && !conv && list_empty(&r->res_convertqueue) &&
first_in_list(lkb, &r->res_waitqueue)) first_in_list(lkb, &r->res_waitqueue))
return 1; return 1;
out: out:
/*
* The following, enabled by CONVDEADLK, departs from VMS.
*/
if (conv && (lkb->lkb_exflags & DLM_LKF_CONVDEADLK) &&
conversion_deadlock_detect(r, lkb)) {
lkb->lkb_grmode = DLM_LOCK_NL;
lkb->lkb_sbflags |= DLM_SBF_DEMOTED;
}
return 0; return 0;
} }
/* static int can_be_granted(struct dlm_rsb *r, struct dlm_lkb *lkb, int now,
* The ALTPR and ALTCW flags aren't traditional lock manager flags, but are a int *err)
* simple way to provide a big optimization to applications that can use them.
*/
static int can_be_granted(struct dlm_rsb *r, struct dlm_lkb *lkb, int now)
{ {
uint32_t flags = lkb->lkb_exflags;
int rv; int rv;
int8_t alt = 0, rqmode = lkb->lkb_rqmode; int8_t alt = 0, rqmode = lkb->lkb_rqmode;
int8_t is_convert = (lkb->lkb_grmode != DLM_LOCK_IV);
if (err)
*err = 0;
rv = _can_be_granted(r, lkb, now); rv = _can_be_granted(r, lkb, now);
if (rv) if (rv)
goto out; goto out;
if (lkb->lkb_sbflags & DLM_SBF_DEMOTED) /*
* The CONVDEADLK flag is non-standard and tells the dlm to resolve
* conversion deadlocks by demoting grmode to NL, otherwise the dlm
* cancels one of the locks.
*/
if (is_convert && can_be_queued(lkb) &&
conversion_deadlock_detect(r, lkb)) {
if (lkb->lkb_exflags & DLM_LKF_CONVDEADLK) {
lkb->lkb_grmode = DLM_LOCK_NL;
lkb->lkb_sbflags |= DLM_SBF_DEMOTED;
} else if (!(lkb->lkb_exflags & DLM_LKF_NODLCKWT)) {
if (err)
*err = -EDEADLK;
else {
log_print("can_be_granted deadlock %x now %d",
lkb->lkb_id, now);
dlm_dump_rsb(r);
}
}
goto out; goto out;
}
if (rqmode != DLM_LOCK_PR && flags & DLM_LKF_ALTPR) /*
* The ALTPR and ALTCW flags are non-standard and tell the dlm to try
* to grant a request in a mode other than the normal rqmode. It's a
* simple way to provide a big optimization to applications that can
* use them.
*/
if (rqmode != DLM_LOCK_PR && (lkb->lkb_exflags & DLM_LKF_ALTPR))
alt = DLM_LOCK_PR; alt = DLM_LOCK_PR;
else if (rqmode != DLM_LOCK_CW && flags & DLM_LKF_ALTCW) else if (rqmode != DLM_LOCK_CW && (lkb->lkb_exflags & DLM_LKF_ALTCW))
alt = DLM_LOCK_CW; alt = DLM_LOCK_CW;
if (alt) { if (alt) {
...@@ -1633,10 +1657,20 @@ static int can_be_granted(struct dlm_rsb *r, struct dlm_lkb *lkb, int now) ...@@ -1633,10 +1657,20 @@ static int can_be_granted(struct dlm_rsb *r, struct dlm_lkb *lkb, int now)
return rv; return rv;
} }
/* FIXME: I don't think that can_be_granted() can/will demote or find deadlock
for locks pending on the convert list. Once verified (watch for these
log_prints), we should be able to just call _can_be_granted() and not
bother with the demote/deadlk cases here (and there's no easy way to deal
with a deadlk here, we'd have to generate something like grant_lock with
the deadlk error.) */
/* returns the highest requested mode of all blocked conversions */
static int grant_pending_convert(struct dlm_rsb *r, int high) static int grant_pending_convert(struct dlm_rsb *r, int high)
{ {
struct dlm_lkb *lkb, *s; struct dlm_lkb *lkb, *s;
int hi, demoted, quit, grant_restart, demote_restart; int hi, demoted, quit, grant_restart, demote_restart;
int deadlk;
quit = 0; quit = 0;
restart: restart:
...@@ -1646,14 +1680,29 @@ static int grant_pending_convert(struct dlm_rsb *r, int high) ...@@ -1646,14 +1680,29 @@ static int grant_pending_convert(struct dlm_rsb *r, int high)
list_for_each_entry_safe(lkb, s, &r->res_convertqueue, lkb_statequeue) { list_for_each_entry_safe(lkb, s, &r->res_convertqueue, lkb_statequeue) {
demoted = is_demoted(lkb); demoted = is_demoted(lkb);
if (can_be_granted(r, lkb, 0)) { deadlk = 0;
if (can_be_granted(r, lkb, 0, &deadlk)) {
grant_lock_pending(r, lkb); grant_lock_pending(r, lkb);
grant_restart = 1; grant_restart = 1;
} else { continue;
hi = max_t(int, lkb->lkb_rqmode, hi);
if (!demoted && is_demoted(lkb))
demote_restart = 1;
} }
if (!demoted && is_demoted(lkb)) {
log_print("WARN: pending demoted %x node %d %s",
lkb->lkb_id, lkb->lkb_nodeid, r->res_name);
demote_restart = 1;
continue;
}
if (deadlk) {
log_print("WARN: pending deadlock %x node %d %s",
lkb->lkb_id, lkb->lkb_nodeid, r->res_name);
dlm_dump_rsb(r);
continue;
}
hi = max_t(int, lkb->lkb_rqmode, hi);
} }
if (grant_restart) if (grant_restart)
...@@ -1671,7 +1720,7 @@ static int grant_pending_wait(struct dlm_rsb *r, int high) ...@@ -1671,7 +1720,7 @@ static int grant_pending_wait(struct dlm_rsb *r, int high)
struct dlm_lkb *lkb, *s; struct dlm_lkb *lkb, *s;
list_for_each_entry_safe(lkb, s, &r->res_waitqueue, lkb_statequeue) { list_for_each_entry_safe(lkb, s, &r->res_waitqueue, lkb_statequeue) {
if (can_be_granted(r, lkb, 0)) if (can_be_granted(r, lkb, 0, NULL))
grant_lock_pending(r, lkb); grant_lock_pending(r, lkb);
else else
high = max_t(int, lkb->lkb_rqmode, high); high = max_t(int, lkb->lkb_rqmode, high);
...@@ -2121,7 +2170,7 @@ static int do_request(struct dlm_rsb *r, struct dlm_lkb *lkb) ...@@ -2121,7 +2170,7 @@ static int do_request(struct dlm_rsb *r, struct dlm_lkb *lkb)
{ {
int error = 0; int error = 0;
if (can_be_granted(r, lkb, 1)) { if (can_be_granted(r, lkb, 1, NULL)) {
grant_lock(r, lkb); grant_lock(r, lkb);
queue_cast(r, lkb, 0); queue_cast(r, lkb, 0);
goto out; goto out;
...@@ -2147,16 +2196,32 @@ static int do_request(struct dlm_rsb *r, struct dlm_lkb *lkb) ...@@ -2147,16 +2196,32 @@ static int do_request(struct dlm_rsb *r, struct dlm_lkb *lkb)
static int do_convert(struct dlm_rsb *r, struct dlm_lkb *lkb) static int do_convert(struct dlm_rsb *r, struct dlm_lkb *lkb)
{ {
int error = 0; int error = 0;
int deadlk = 0;
/* changing an existing lock may allow others to be granted */ /* changing an existing lock may allow others to be granted */
if (can_be_granted(r, lkb, 1)) { if (can_be_granted(r, lkb, 1, &deadlk)) {
grant_lock(r, lkb); grant_lock(r, lkb);
queue_cast(r, lkb, 0); queue_cast(r, lkb, 0);
grant_pending_locks(r); grant_pending_locks(r);
goto out; goto out;
} }
/* can_be_granted() detected that this lock would block in a conversion
deadlock, so we leave it on the granted queue and return EDEADLK in
the ast for the convert. */
if (deadlk) {
/* it's left on the granted queue */
log_debug(r->res_ls, "deadlock %x node %d sts%d g%d r%d %s",
lkb->lkb_id, lkb->lkb_nodeid, lkb->lkb_status,
lkb->lkb_grmode, lkb->lkb_rqmode, r->res_name);
revert_lock(r, lkb);
queue_cast(r, lkb, -EDEADLK);
error = -EDEADLK;
goto out;
}
/* is_demoted() means the can_be_granted() above set the grmode /* is_demoted() means the can_be_granted() above set the grmode
to NL, and left us on the granted queue. This auto-demotion to NL, and left us on the granted queue. This auto-demotion
(due to CONVDEADLK) might mean other locks, and/or this lock, are (due to CONVDEADLK) might mean other locks, and/or this lock, are
...@@ -2438,7 +2503,7 @@ int dlm_lock(dlm_lockspace_t *lockspace, ...@@ -2438,7 +2503,7 @@ int dlm_lock(dlm_lockspace_t *lockspace,
out_put: out_put:
if (convert || error) if (convert || error)
__put_lkb(ls, lkb); __put_lkb(ls, lkb);
if (error == -EAGAIN) if (error == -EAGAIN || error == -EDEADLK)
error = 0; error = 0;
out: out:
dlm_unlock_recovery(ls); dlm_unlock_recovery(ls);
...@@ -3312,6 +3377,12 @@ static void __receive_convert_reply(struct dlm_rsb *r, struct dlm_lkb *lkb, ...@@ -3312,6 +3377,12 @@ static void __receive_convert_reply(struct dlm_rsb *r, struct dlm_lkb *lkb,
queue_cast(r, lkb, -EAGAIN); queue_cast(r, lkb, -EAGAIN);
break; break;
case -EDEADLK:
receive_flags_reply(lkb, ms);
revert_lock_pc(r, lkb);
queue_cast(r, lkb, -EDEADLK);
break;
case -EINPROGRESS: case -EINPROGRESS:
/* convert was queued on remote master */ /* convert was queued on remote master */
receive_flags_reply(lkb, ms); receive_flags_reply(lkb, ms);
...@@ -4284,7 +4355,7 @@ int dlm_user_convert(struct dlm_ls *ls, struct dlm_user_args *ua_tmp, ...@@ -4284,7 +4355,7 @@ int dlm_user_convert(struct dlm_ls *ls, struct dlm_user_args *ua_tmp,
error = convert_lock(ls, lkb, &args); error = convert_lock(ls, lkb, &args);
if (error == -EINPROGRESS || error == -EAGAIN) if (error == -EINPROGRESS || error == -EAGAIN || error == -EDEADLK)
error = 0; error = 0;
out_put: out_put:
dlm_put_lkb(lkb); dlm_put_lkb(lkb);
......
...@@ -85,7 +85,11 @@ ...@@ -85,7 +85,11 @@
* Only relevant to locks originating in userspace. A persistent lock will not * Only relevant to locks originating in userspace. A persistent lock will not
* be removed if the process holding the lock exits. * be removed if the process holding the lock exits.
* *
* DLM_LKF_NODLKWT * DLM_LKF_NODLCKWT
*
* Do not cancel the lock if it gets into conversion deadlock.
* Exclude this lock from being monitored due to DLM_LSFL_TIMEWARN.
*
* DLM_LKF_NODLCKBLK * DLM_LKF_NODLCKBLK
* *
* net yet implemented * net yet implemented
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册