📄 dlmglue.c
字号:
* once on it manually. */ ocfs2_lock_res_init_once(res); ocfs2_build_lock_name(OCFS2_LOCK_TYPE_SUPER, OCFS2_SUPER_BLOCK_BLKNO, 0, res->l_name); ocfs2_lock_res_init_common(osb, res, OCFS2_LOCK_TYPE_SUPER, &ocfs2_super_lops, osb);}static void ocfs2_rename_lock_res_init(struct ocfs2_lock_res *res, struct ocfs2_super *osb){ /* Rename lockres doesn't come from a slab so we call init * once on it manually. */ ocfs2_lock_res_init_once(res); ocfs2_build_lock_name(OCFS2_LOCK_TYPE_RENAME, 0, 0, res->l_name); ocfs2_lock_res_init_common(osb, res, OCFS2_LOCK_TYPE_RENAME, &ocfs2_rename_lops, osb);}void ocfs2_lock_res_free(struct ocfs2_lock_res *res){ mlog_entry_void(); if (!(res->l_flags & OCFS2_LOCK_INITIALIZED)) return; ocfs2_remove_lockres_tracking(res); mlog_bug_on_msg(!list_empty(&res->l_blocked_list), "Lockres %s is on the blocked list\n", res->l_name); mlog_bug_on_msg(!list_empty(&res->l_mask_waiters), "Lockres %s has mask waiters pending\n", res->l_name); mlog_bug_on_msg(spin_is_locked(&res->l_lock), "Lockres %s is locked\n", res->l_name); mlog_bug_on_msg(res->l_ro_holders, "Lockres %s has %u ro holders\n", res->l_name, res->l_ro_holders); mlog_bug_on_msg(res->l_ex_holders, "Lockres %s has %u ex holders\n", res->l_name, res->l_ex_holders); /* Need to clear out the lock status block for the dlm */ memset(&res->l_lksb, 0, sizeof(res->l_lksb)); res->l_flags = 0UL; mlog_exit_void();}static inline void ocfs2_inc_holders(struct ocfs2_lock_res *lockres, int level){ mlog_entry_void(); BUG_ON(!lockres); switch(level) { case LKM_EXMODE: lockres->l_ex_holders++; break; case LKM_PRMODE: lockres->l_ro_holders++; break; default: BUG(); } mlog_exit_void();}static inline void ocfs2_dec_holders(struct ocfs2_lock_res *lockres, int level){ mlog_entry_void(); BUG_ON(!lockres); switch(level) { case LKM_EXMODE: BUG_ON(!lockres->l_ex_holders); lockres->l_ex_holders--; break; case LKM_PRMODE: BUG_ON(!lockres->l_ro_holders); lockres->l_ro_holders--; break; default: BUG(); } mlog_exit_void();}/* WARNING: This function lives in a world where the only three lock * levels are EX, PR, and NL. It *will* have to be adjusted when more * lock types are added. */static inline int ocfs2_highest_compat_lock_level(int level){ int new_level = LKM_EXMODE; if (level == LKM_EXMODE) new_level = LKM_NLMODE; else if (level == LKM_PRMODE) new_level = LKM_PRMODE; return new_level;}static void lockres_set_flags(struct ocfs2_lock_res *lockres, unsigned long newflags){ struct ocfs2_mask_waiter *mw, *tmp; assert_spin_locked(&lockres->l_lock); lockres->l_flags = newflags; list_for_each_entry_safe(mw, tmp, &lockres->l_mask_waiters, mw_item) { if ((lockres->l_flags & mw->mw_mask) != mw->mw_goal) continue; list_del_init(&mw->mw_item); mw->mw_status = 0; complete(&mw->mw_complete); }}static void lockres_or_flags(struct ocfs2_lock_res *lockres, unsigned long or){ lockres_set_flags(lockres, lockres->l_flags | or);}static void lockres_clear_flags(struct ocfs2_lock_res *lockres, unsigned long clear){ lockres_set_flags(lockres, lockres->l_flags & ~clear);}static inline void ocfs2_generic_handle_downconvert_action(struct ocfs2_lock_res *lockres){ mlog_entry_void(); BUG_ON(!(lockres->l_flags & OCFS2_LOCK_BUSY)); BUG_ON(!(lockres->l_flags & OCFS2_LOCK_ATTACHED)); BUG_ON(!(lockres->l_flags & OCFS2_LOCK_BLOCKED)); BUG_ON(lockres->l_blocking <= LKM_NLMODE); lockres->l_level = lockres->l_requested; if (lockres->l_level <= ocfs2_highest_compat_lock_level(lockres->l_blocking)) { lockres->l_blocking = LKM_NLMODE; lockres_clear_flags(lockres, OCFS2_LOCK_BLOCKED); } lockres_clear_flags(lockres, OCFS2_LOCK_BUSY); mlog_exit_void();}static inline void ocfs2_generic_handle_convert_action(struct ocfs2_lock_res *lockres){ mlog_entry_void(); BUG_ON(!(lockres->l_flags & OCFS2_LOCK_BUSY)); BUG_ON(!(lockres->l_flags & OCFS2_LOCK_ATTACHED)); /* Convert from RO to EX doesn't really need anything as our * information is already up to data. Convert from NL to * *anything* however should mark ourselves as needing an * update */ if (lockres->l_level == LKM_NLMODE && lockres->l_ops->flags & LOCK_TYPE_REQUIRES_REFRESH) lockres_or_flags(lockres, OCFS2_LOCK_NEEDS_REFRESH); lockres->l_level = lockres->l_requested; lockres_clear_flags(lockres, OCFS2_LOCK_BUSY); mlog_exit_void();}static inline void ocfs2_generic_handle_attach_action(struct ocfs2_lock_res *lockres){ mlog_entry_void(); BUG_ON((!(lockres->l_flags & OCFS2_LOCK_BUSY))); BUG_ON(lockres->l_flags & OCFS2_LOCK_ATTACHED); if (lockres->l_requested > LKM_NLMODE && !(lockres->l_flags & OCFS2_LOCK_LOCAL) && lockres->l_ops->flags & LOCK_TYPE_REQUIRES_REFRESH) lockres_or_flags(lockres, OCFS2_LOCK_NEEDS_REFRESH); lockres->l_level = lockres->l_requested; lockres_or_flags(lockres, OCFS2_LOCK_ATTACHED); lockres_clear_flags(lockres, OCFS2_LOCK_BUSY); mlog_exit_void();}static int ocfs2_generic_handle_bast(struct ocfs2_lock_res *lockres, int level){ int needs_downconvert = 0; mlog_entry_void(); assert_spin_locked(&lockres->l_lock); lockres_or_flags(lockres, OCFS2_LOCK_BLOCKED); if (level > lockres->l_blocking) { /* only schedule a downconvert if we haven't already scheduled * one that goes low enough to satisfy the level we're * blocking. this also catches the case where we get * duplicate BASTs */ if (ocfs2_highest_compat_lock_level(level) < ocfs2_highest_compat_lock_level(lockres->l_blocking)) needs_downconvert = 1; lockres->l_blocking = level; } mlog_exit(needs_downconvert); return needs_downconvert;}static void ocfs2_blocking_ast(void *opaque, int level){ struct ocfs2_lock_res *lockres = opaque; struct ocfs2_super *osb = ocfs2_get_lockres_osb(lockres); int needs_downconvert; unsigned long flags; BUG_ON(level <= LKM_NLMODE); mlog(0, "BAST fired for lockres %s, blocking %d, level %d type %s\n", lockres->l_name, level, lockres->l_level, ocfs2_lock_type_string(lockres->l_type)); spin_lock_irqsave(&lockres->l_lock, flags); needs_downconvert = ocfs2_generic_handle_bast(lockres, level); if (needs_downconvert) ocfs2_schedule_blocked_lock(osb, lockres); spin_unlock_irqrestore(&lockres->l_lock, flags); wake_up(&lockres->l_event); ocfs2_kick_vote_thread(osb);}static void ocfs2_locking_ast(void *opaque){ struct ocfs2_lock_res *lockres = opaque; struct dlm_lockstatus *lksb = &lockres->l_lksb; unsigned long flags; spin_lock_irqsave(&lockres->l_lock, flags); if (lksb->status != DLM_NORMAL) { mlog(ML_ERROR, "lockres %s: lksb status value of %u!\n", lockres->l_name, lksb->status); spin_unlock_irqrestore(&lockres->l_lock, flags); return; } switch(lockres->l_action) { case OCFS2_AST_ATTACH: ocfs2_generic_handle_attach_action(lockres); lockres_clear_flags(lockres, OCFS2_LOCK_LOCAL); break; case OCFS2_AST_CONVERT: ocfs2_generic_handle_convert_action(lockres); break; case OCFS2_AST_DOWNCONVERT: ocfs2_generic_handle_downconvert_action(lockres); break; default: mlog(ML_ERROR, "lockres %s: ast fired with invalid action: %u " "lockres flags = 0x%lx, unlock action: %u\n", lockres->l_name, lockres->l_action, lockres->l_flags, lockres->l_unlock_action); BUG(); } /* set it to something invalid so if we get called again we * can catch it. */ lockres->l_action = OCFS2_AST_INVALID; wake_up(&lockres->l_event); spin_unlock_irqrestore(&lockres->l_lock, flags);}static inline void ocfs2_recover_from_dlm_error(struct ocfs2_lock_res *lockres, int convert){ unsigned long flags; mlog_entry_void(); spin_lock_irqsave(&lockres->l_lock, flags); lockres_clear_flags(lockres, OCFS2_LOCK_BUSY); if (convert) lockres->l_action = OCFS2_AST_INVALID; else lockres->l_unlock_action = OCFS2_UNLOCK_INVALID; spin_unlock_irqrestore(&lockres->l_lock, flags); wake_up(&lockres->l_event); mlog_exit_void();}/* Note: If we detect another process working on the lock (i.e., * OCFS2_LOCK_BUSY), we'll bail out returning 0. It's up to the caller * to do the right thing in that case. */static int ocfs2_lock_create(struct ocfs2_super *osb, struct ocfs2_lock_res *lockres, int level, int dlm_flags){ int ret = 0; enum dlm_status status = DLM_NORMAL; unsigned long flags; mlog_entry_void(); mlog(0, "lock %s, level = %d, flags = %d\n", lockres->l_name, level, dlm_flags); spin_lock_irqsave(&lockres->l_lock, flags); if ((lockres->l_flags & OCFS2_LOCK_ATTACHED) || (lockres->l_flags & OCFS2_LOCK_BUSY)) { spin_unlock_irqrestore(&lockres->l_lock, flags); goto bail; } lockres->l_action = OCFS2_AST_ATTACH; lockres->l_requested = level; lockres_or_flags(lockres, OCFS2_LOCK_BUSY); spin_unlock_irqrestore(&lockres->l_lock, flags); status = dlmlock(osb->dlm, level, &lockres->l_lksb, dlm_flags, lockres->l_name, OCFS2_LOCK_ID_MAX_LEN - 1, ocfs2_locking_ast, lockres, ocfs2_blocking_ast); if (status != DLM_NORMAL) { ocfs2_log_dlm_error("dlmlock", status, lockres); ret = -EINVAL; ocfs2_recover_from_dlm_error(lockres, 1); } mlog(0, "lock %s, successfull return from dlmlock\n", lockres->l_name);bail: mlog_exit(ret); return ret;}static inline int ocfs2_check_wait_flag(struct ocfs2_lock_res *lockres, int flag){ unsigned long flags; int ret; spin_lock_irqsave(&lockres->l_lock, flags); ret = lockres->l_flags & flag; spin_unlock_irqrestore(&lockres->l_lock, flags); return ret;}static inline void ocfs2_wait_on_busy_lock(struct ocfs2_lock_res *lockres){ wait_event(lockres->l_event, !ocfs2_check_wait_flag(lockres, OCFS2_LOCK_BUSY));}static inline void ocfs2_wait_on_refreshing_lock(struct ocfs2_lock_res *lockres){ wait_event(lockres->l_event, !ocfs2_check_wait_flag(lockres, OCFS2_LOCK_REFRESHING));}/* predict what lock level we'll be dropping down to on behalf * of another node, and return true if the currently wanted * level will be compatible with it. */static inline int ocfs2_may_continue_on_blocked_lock(struct ocfs2_lock_res *lockres, int wanted){ BUG_ON(!(lockres->l_flags & OCFS2_LOCK_BLOCKED)); return wanted <= ocfs2_highest_compat_lock_level(lockres->l_blocking);}static void ocfs2_init_mask_waiter(struct ocfs2_mask_waiter *mw){ INIT_LIST_HEAD(&mw->mw_item); init_completion(&mw->mw_complete);}static int ocfs2_wait_for_mask(struct ocfs2_mask_waiter *mw){ wait_for_completion(&mw->mw_complete); /* Re-arm the completion in case we want to wait on it again */ INIT_COMPLETION(mw->mw_complete); return mw->mw_status;}static void lockres_add_mask_waiter(struct ocfs2_lock_res *lockres, struct ocfs2_mask_waiter *mw, unsigned long mask, unsigned long goal){ BUG_ON(!list_empty(&mw->mw_item)); assert_spin_locked(&lockres->l_lock); list_add_tail(&mw->mw_item, &lockres->l_mask_waiters); mw->mw_mask = mask; mw->mw_goal = goal;}/* returns 0 if the mw that was removed was already satisfied, -EBUSY * if the mask still hadn't reached its goal */static int lockres_remove_mask_waiter(struct ocfs2_lock_res *lockres, struct ocfs2_mask_waiter *mw){ unsigned long flags; int ret = 0; spin_lock_irqsave(&lockres->l_lock, flags); if (!list_empty(&mw->mw_item)) { if ((lockres->l_flags & mw->mw_mask) != mw->mw_goal) ret = -EBUSY; list_del_init(&mw->mw_item); init_completion(&mw->mw_complete); } spin_unlock_irqrestore(&lockres->l_lock, flags); return ret;}static int ocfs2_cluster_lock(struct ocfs2_super *osb, struct ocfs2_lock_res *lockres, int level, int lkm_flags, int arg_flags){ struct ocfs2_mask_waiter mw; enum dlm_status status; int wait, catch_signals = !(osb->s_mount_opt & OCFS2_MOUNT_NOINTR); int ret = 0; /* gcc doesn't realize wait = 1 guarantees ret is set */ unsigned long flags; mlog_entry_void(); ocfs2_init_mask_waiter(&mw); if (lockres->l_ops->flags & LOCK_TYPE_USES_LVB) lkm_flags |= LKM_VALBLK;again: wait = 0; if (catch_signals && signal_pending(current)) { ret = -ERESTARTSYS; goto out; } spin_lock_irqsave(&lockres->l_lock, flags); mlog_bug_on_msg(lockres->l_flags & OCFS2_LOCK_FREEING, "Cluster lock called on freeing lockres %s! flags " "0x%lx\n", lockres->l_name, lockres->l_flags); /* We only compare against the currently granted level * here. If the lock is blocked waiting on a downconvert, * we'll get caught below. */ if (lockres->l_flags & OCFS2_LOCK_BUSY && level > lockres->l_level) { /* is someone sitting in dlm_lock? If so, wait on * them. */ lockres_add_mask_waiter(lockres, &mw, OCFS2_LOCK_BUSY, 0); wait = 1; goto unlock; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -