📄 lock.c
字号:
} if (test_bit(LSFL_TIMEWARN, &ls->ls_flags) && !(lkb->lkb_exflags & DLM_LKF_NODLCKWT)) { lkb->lkb_flags |= DLM_IFL_WATCH_TIMEWARN; goto add_it; } if (lkb->lkb_exflags & DLM_LKF_TIMEOUT) goto add_it; return; add_it: DLM_ASSERT(list_empty(&lkb->lkb_time_list), dlm_print_lkb(lkb);); mutex_lock(&ls->ls_timeout_mutex); hold_lkb(lkb); lkb->lkb_timestamp = jiffies; list_add_tail(&lkb->lkb_time_list, &ls->ls_timeout); mutex_unlock(&ls->ls_timeout_mutex);}static void del_timeout(struct dlm_lkb *lkb){ struct dlm_ls *ls = lkb->lkb_resource->res_ls; mutex_lock(&ls->ls_timeout_mutex); if (!list_empty(&lkb->lkb_time_list)) { list_del_init(&lkb->lkb_time_list); unhold_lkb(lkb); } mutex_unlock(&ls->ls_timeout_mutex);}/* FIXME: is it safe to look at lkb_exflags, lkb_flags, lkb_timestamp, and lkb_lksb_timeout without lock_rsb? Note: we can't lock timeout_mutex and then lock rsb because of lock ordering in add_timeout. We may need to specify some special timeout-related bits in the lkb that are just to be accessed under the timeout_mutex. */void dlm_scan_timeout(struct dlm_ls *ls){ struct dlm_rsb *r; struct dlm_lkb *lkb; int do_cancel, do_warn; for (;;) { if (dlm_locking_stopped(ls)) break; do_cancel = 0; do_warn = 0; mutex_lock(&ls->ls_timeout_mutex); list_for_each_entry(lkb, &ls->ls_timeout, lkb_time_list) { if ((lkb->lkb_exflags & DLM_LKF_TIMEOUT) && time_after_eq(jiffies, lkb->lkb_timestamp + lkb->lkb_timeout_cs * HZ/100)) do_cancel = 1; if ((lkb->lkb_flags & DLM_IFL_WATCH_TIMEWARN) && time_after_eq(jiffies, lkb->lkb_timestamp + dlm_config.ci_timewarn_cs * HZ/100)) do_warn = 1; if (!do_cancel && !do_warn) continue; hold_lkb(lkb); break; } mutex_unlock(&ls->ls_timeout_mutex); if (!do_cancel && !do_warn) break; r = lkb->lkb_resource; hold_rsb(r); lock_rsb(r); if (do_warn) { /* clear flag so we only warn once */ lkb->lkb_flags &= ~DLM_IFL_WATCH_TIMEWARN; if (!(lkb->lkb_exflags & DLM_LKF_TIMEOUT)) del_timeout(lkb); dlm_timeout_warn(lkb); } if (do_cancel) { log_debug(ls, "timeout cancel %x node %d %s", lkb->lkb_id, lkb->lkb_nodeid, r->res_name); lkb->lkb_flags &= ~DLM_IFL_WATCH_TIMEWARN; lkb->lkb_flags |= DLM_IFL_TIMEOUT_CANCEL; del_timeout(lkb); _cancel_lock(r, lkb); } unlock_rsb(r); unhold_rsb(r); dlm_put_lkb(lkb); }}/* This is only called by dlm_recoverd, and we rely on dlm_ls_stop() stopping dlm_recoverd before checking/setting ls_recover_begin. */void dlm_adjust_timeouts(struct dlm_ls *ls){ struct dlm_lkb *lkb; long adj = jiffies - ls->ls_recover_begin; ls->ls_recover_begin = 0; mutex_lock(&ls->ls_timeout_mutex); list_for_each_entry(lkb, &ls->ls_timeout, lkb_time_list) lkb->lkb_timestamp += adj; mutex_unlock(&ls->ls_timeout_mutex);}/* lkb is master or local copy */static void set_lvb_lock(struct dlm_rsb *r, struct dlm_lkb *lkb){ int b, len = r->res_ls->ls_lvblen; /* b=1 lvb returned to caller b=0 lvb written to rsb or invalidated b=-1 do nothing */ b = dlm_lvb_operations[lkb->lkb_grmode + 1][lkb->lkb_rqmode + 1]; if (b == 1) { if (!lkb->lkb_lvbptr) return; if (!(lkb->lkb_exflags & DLM_LKF_VALBLK)) return; if (!r->res_lvbptr) return; memcpy(lkb->lkb_lvbptr, r->res_lvbptr, len); lkb->lkb_lvbseq = r->res_lvbseq; } else if (b == 0) { if (lkb->lkb_exflags & DLM_LKF_IVVALBLK) { rsb_set_flag(r, RSB_VALNOTVALID); return; } if (!lkb->lkb_lvbptr) return; if (!(lkb->lkb_exflags & DLM_LKF_VALBLK)) return; if (!r->res_lvbptr) r->res_lvbptr = allocate_lvb(r->res_ls); if (!r->res_lvbptr) return; memcpy(r->res_lvbptr, lkb->lkb_lvbptr, len); r->res_lvbseq++; lkb->lkb_lvbseq = r->res_lvbseq; rsb_clear_flag(r, RSB_VALNOTVALID); } if (rsb_flag(r, RSB_VALNOTVALID)) lkb->lkb_sbflags |= DLM_SBF_VALNOTVALID;}static void set_lvb_unlock(struct dlm_rsb *r, struct dlm_lkb *lkb){ if (lkb->lkb_grmode < DLM_LOCK_PW) return; if (lkb->lkb_exflags & DLM_LKF_IVVALBLK) { rsb_set_flag(r, RSB_VALNOTVALID); return; } if (!lkb->lkb_lvbptr) return; if (!(lkb->lkb_exflags & DLM_LKF_VALBLK)) return; if (!r->res_lvbptr) r->res_lvbptr = allocate_lvb(r->res_ls); if (!r->res_lvbptr) return; memcpy(r->res_lvbptr, lkb->lkb_lvbptr, r->res_ls->ls_lvblen); r->res_lvbseq++; rsb_clear_flag(r, RSB_VALNOTVALID);}/* lkb is process copy (pc) */static void set_lvb_lock_pc(struct dlm_rsb *r, struct dlm_lkb *lkb, struct dlm_message *ms){ int b; if (!lkb->lkb_lvbptr) return; if (!(lkb->lkb_exflags & DLM_LKF_VALBLK)) return; b = dlm_lvb_operations[lkb->lkb_grmode + 1][lkb->lkb_rqmode + 1]; if (b == 1) { int len = receive_extralen(ms); memcpy(lkb->lkb_lvbptr, ms->m_extra, len); lkb->lkb_lvbseq = ms->m_lvbseq; }}/* Manipulate lkb's on rsb's convert/granted/waiting queues remove_lock -- used for unlock, removes lkb from granted revert_lock -- used for cancel, moves lkb from convert to granted grant_lock -- used for request and convert, adds lkb to granted or moves lkb from convert or waiting to granted Each of these is used for master or local copy lkb's. There is also a _pc() variation used to make the corresponding change on a process copy (pc) lkb. */static void _remove_lock(struct dlm_rsb *r, struct dlm_lkb *lkb){ del_lkb(r, lkb); lkb->lkb_grmode = DLM_LOCK_IV; /* this unhold undoes the original ref from create_lkb() so this leads to the lkb being freed */ unhold_lkb(lkb);}static void remove_lock(struct dlm_rsb *r, struct dlm_lkb *lkb){ set_lvb_unlock(r, lkb); _remove_lock(r, lkb);}static void remove_lock_pc(struct dlm_rsb *r, struct dlm_lkb *lkb){ _remove_lock(r, lkb);}/* returns: 0 did nothing 1 moved lock to granted -1 removed lock */static int revert_lock(struct dlm_rsb *r, struct dlm_lkb *lkb){ int rv = 0; lkb->lkb_rqmode = DLM_LOCK_IV; switch (lkb->lkb_status) { case DLM_LKSTS_GRANTED: break; case DLM_LKSTS_CONVERT: move_lkb(r, lkb, DLM_LKSTS_GRANTED); rv = 1; break; case DLM_LKSTS_WAITING: del_lkb(r, lkb); lkb->lkb_grmode = DLM_LOCK_IV; /* this unhold undoes the original ref from create_lkb() so this leads to the lkb being freed */ unhold_lkb(lkb); rv = -1; break; default: log_print("invalid status for revert %d", lkb->lkb_status); } return rv;}static int revert_lock_pc(struct dlm_rsb *r, struct dlm_lkb *lkb){ return revert_lock(r, lkb);}static void _grant_lock(struct dlm_rsb *r, struct dlm_lkb *lkb){ if (lkb->lkb_grmode != lkb->lkb_rqmode) { lkb->lkb_grmode = lkb->lkb_rqmode; if (lkb->lkb_status) move_lkb(r, lkb, DLM_LKSTS_GRANTED); else add_lkb(r, lkb, DLM_LKSTS_GRANTED); } lkb->lkb_rqmode = DLM_LOCK_IV;}static void grant_lock(struct dlm_rsb *r, struct dlm_lkb *lkb){ set_lvb_lock(r, lkb); _grant_lock(r, lkb); lkb->lkb_highbast = 0;}static void grant_lock_pc(struct dlm_rsb *r, struct dlm_lkb *lkb, struct dlm_message *ms){ set_lvb_lock_pc(r, lkb, ms); _grant_lock(r, lkb);}/* called by grant_pending_locks() which means an async grant message must be sent to the requesting node in addition to granting the lock if the lkb belongs to a remote node. */static void grant_lock_pending(struct dlm_rsb *r, struct dlm_lkb *lkb){ grant_lock(r, lkb); if (is_master_copy(lkb)) send_grant(r, lkb); else queue_cast(r, lkb, 0);}/* The special CONVDEADLK, ALTPR and ALTCW flags allow the master to change the granted/requested modes. We're munging things accordingly in the process copy. CONVDEADLK: our grmode may have been forced down to NL to resolve a conversion deadlock ALTPR/ALTCW: our rqmode may have been changed to PR or CW to become compatible with other granted locks */static void munge_demoted(struct dlm_lkb *lkb, struct dlm_message *ms){ if (ms->m_type != DLM_MSG_CONVERT_REPLY) { log_print("munge_demoted %x invalid reply type %d", lkb->lkb_id, ms->m_type); return; } if (lkb->lkb_rqmode == DLM_LOCK_IV || lkb->lkb_grmode == DLM_LOCK_IV) { log_print("munge_demoted %x invalid modes gr %d rq %d", lkb->lkb_id, lkb->lkb_grmode, lkb->lkb_rqmode); return; } lkb->lkb_grmode = DLM_LOCK_NL;}static void munge_altmode(struct dlm_lkb *lkb, struct dlm_message *ms){ if (ms->m_type != DLM_MSG_REQUEST_REPLY && ms->m_type != DLM_MSG_GRANT) { log_print("munge_altmode %x invalid reply type %d", lkb->lkb_id, ms->m_type); return; } if (lkb->lkb_exflags & DLM_LKF_ALTPR) lkb->lkb_rqmode = DLM_LOCK_PR; else if (lkb->lkb_exflags & DLM_LKF_ALTCW) lkb->lkb_rqmode = DLM_LOCK_CW; else { log_print("munge_altmode invalid exflags %x", lkb->lkb_exflags); dlm_print_lkb(lkb); }}static inline int first_in_list(struct dlm_lkb *lkb, struct list_head *head){ struct dlm_lkb *first = list_entry(head->next, struct dlm_lkb, lkb_statequeue); if (lkb->lkb_id == first->lkb_id) return 1; return 0;}/* Check if the given lkb conflicts with another lkb on the queue. */static int queue_conflict(struct list_head *head, struct dlm_lkb *lkb){ struct dlm_lkb *this; list_for_each_entry(this, head, lkb_statequeue) { if (this == lkb) continue; if (!modes_compat(this, lkb)) return 1; } return 0;}/* * "A conversion deadlock arises with a pair of lock requests in the converting * queue for one resource. The granted mode of each lock blocks the requested * mode of the other lock." * * Part 2: if the granted mode of lkb is preventing an earlier lkb in the * convert queue from being granted, then deadlk/demote lkb. * * Example: * Granted Queue: empty * Convert Queue: NL->EX (first lock) * PR->EX (second lock) * * 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 * list. We either cancel lkb's conversion (PR->EX) and return EDEADLK, or we * demote the granted mode of lkb (from PR to NL) if it has the CONVDEADLK * flag set and return DEMOTED in the lksb flags. * * Originally, this function detected conv-deadlk in a more limited scope: * - if !modes_compat(lkb1, lkb2) && !modes_compat(lkb2, lkb1), or * - 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 *r, struct dlm_lkb *lkb2){ struct dlm_lkb *lkb1; int lkb_is_ahead = 0; list_for_each_entry(lkb1, &r->res_convertqueue, lkb_statequeue) { if (lkb1 == lkb2) { lkb_is_ahead = 1; continue; } if (!lkb_is_ahead) { if (!modes_compat(lkb2, lkb1)) return 1; } else { if (!modes_compat(lkb2, lkb1) && !modes_compat(lkb1, lkb2)) return 1; } } return 0;}/* * Return 1 if the lock can be granted, 0 otherwise. * Also detect and resolve conversion deadlocks. * * lkb is the lock to be granted * * now is 1 if the function is being called in the context of the * immediate request, it is 0 if called later, after the lock has been * queued. * * References are from chapter 6 of "VAXcluster Principles" by Roy Davis */static int _can_be_granted(struct dlm_rsb *r, struct dlm_lkb *lkb, int now){ int8_t conv = (lkb->lkb_grmode != DLM_LOCK_IV); /* * 6-10: Version 5.4 introduced an option to address the phenomenon of * a new request for a NL mode lock being blocked. * * 6-11: If the optional EXPEDITE flag is used with the new NL mode * request, then it would be granted. In essence, the use of this flag * tells the Lock Manager to expedite theis request by not considering * what may be in the CONVERTING or WAITING queues... As of this * writing, the EXPEDITE flag can be used only with new requests for NL * mode locks. This flag is not valid for conversion requests. * * A shortcut. Earlier checks return an error if EXPEDITE is used in a * conversion or used with a non-NL requested mode. We also know an * EXPEDITE request is always granted immediately, so now must always * be 1. The full condition to grant an expedite request: (now && * !conv && lkb->rqmode == DLM_LOCK_NL && (flags & EXPEDITE)) can * therefore be shortened to just checking the flag. */ if (lkb->lkb_exflags & DLM_LKF_EXPEDITE) return 1; /* * A shortcut. Without this, !queue_conflict(grantqueue, lkb) would be * added to the remaining conditions. */ if (queue_conflict(&r->res_grantqueue, lkb)) goto out; /* * 6-3: By default, a conversion request is immediately granted if the * requested mode is compatible with the modes of all other granted * locks */ if (queue_conflict(&r->res_convertqueue, lkb)) goto out;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -