/* * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU General Public License version 2. */ #include "lock_dlm.h" /* A lock placed on this queue is re-submitted to DLM as soon as the lock_dlm thread gets to it. */ static void queue_submit(struct gdlm_lock *lp) { struct gdlm_ls *ls = lp->ls; spin_lock(&ls->async_lock); list_add_tail(&lp->delay_list, &ls->submit); spin_unlock(&ls->async_lock); wake_up(&ls->thread_wait); } static void process_blocking(struct gdlm_lock *lp, int bast_mode) { struct gdlm_ls *ls = lp->ls; unsigned int cb = 0; switch (gdlm_make_lmstate(bast_mode)) { case LM_ST_EXCLUSIVE: cb = LM_CB_NEED_E; break; case LM_ST_DEFERRED: cb = LM_CB_NEED_D; break; case LM_ST_SHARED: cb = LM_CB_NEED_S; break; default: gdlm_assert(0, "unknown bast mode %u", lp->bast_mode); } ls->fscb(ls->sdp, cb, &lp->lockname); } static void process_complete(struct gdlm_lock *lp) { struct gdlm_ls *ls = lp->ls; struct lm_async_cb acb; s16 prev_mode = lp->cur; memset(&acb, 0, sizeof(acb)); if (lp->lksb.sb_status == -DLM_ECANCEL) { log_info("complete dlm cancel %x,%llx flags %lx", lp->lockname.ln_type, (unsigned long long)lp->lockname.ln_number, lp->flags); lp->req = lp->cur; acb.lc_ret |= LM_OUT_CANCELED; if (lp->cur == DLM_LOCK_IV) lp->lksb.sb_lkid = 0; goto out; } if (test_and_clear_bit(LFL_DLM_UNLOCK, &lp->flags)) { if (lp->lksb.sb_status != -DLM_EUNLOCK) { log_info("unlock sb_status %d %x,%llx flags %lx", lp->lksb.sb_status, lp->lockname.ln_type, (unsigned long long)lp->lockname.ln_number, lp->flags); return; } lp->cur = DLM_LOCK_IV; lp->req = DLM_LOCK_IV; lp->lksb.sb_lkid = 0; if (test_and_clear_bit(LFL_UNLOCK_DELETE, &lp->flags)) { gdlm_delete_lp(lp); return; } goto out; } if (lp->lksb.sb_flags & DLM_SBF_VALNOTVALID) memset(lp->lksb.sb_lvbptr, 0, GDLM_LVB_SIZE); if (lp->lksb.sb_flags & DLM_SBF_ALTMODE) { if (lp->req == DLM_LOCK_PR) lp->req = DLM_LOCK_CW; else if (lp->req == DLM_LOCK_CW) lp->req = DLM_LOCK_PR; } /* * A canceled lock request. The lock was just taken off the delayed * list and was never even submitted to dlm. */ if (test_and_clear_bit(LFL_CANCEL, &lp->flags)) { log_info("complete internal cancel %x,%llx", lp->lockname.ln_type, (unsigned long long)lp->lockname.ln_number); lp->req = lp->cur; acb.lc_ret |= LM_OUT_CANCELED; goto out; } /* * An error occured. */ if (lp->lksb.sb_status) { /* a "normal" error */ if ((lp->lksb.sb_status == -EAGAIN) && (lp->lkf & DLM_LKF_NOQUEUE)) { lp->req = lp->cur; if (lp->cur == DLM_LOCK_IV) lp->lksb.sb_lkid = 0; goto out; } /* this could only happen with cancels I think */ log_info("ast sb_status %d %x,%llx flags %lx", lp->lksb.sb_status, lp->lockname.ln_type, (unsigned long long)lp->lockname.ln_number, lp->flags); return; } /* * This is an AST for an EX->EX conversion for sync_lvb from GFS. */ if (test_and_clear_bit(LFL_SYNC_LVB, &lp->flags)) { complete(&lp->ast_wait); return; } /* * A lock has been demoted to NL because it initially completed during * BLOCK_LOCKS. Now it must be requested in the originally requested * mode. */ if (test_and_clear_bit(LFL_REREQUEST, &lp->flags)) { gdlm_assert(lp->req == DLM_LOCK_NL, "%x,%llx", lp->lockname.ln_type, (unsigned long long)lp->lockname.ln_number); gdlm_assert(lp->prev_req > DLM_LOCK_NL, "%x,%llx", lp->lockname.ln_type, (unsigned long long)lp->lockname.ln_number); lp->cur = DLM_LOCK_NL; lp->req = lp->prev_req; lp->prev_req = DLM_LOCK_IV; lp->lkf &= ~DLM_LKF_CONVDEADLK; set_bit(LFL_NOCACHE, &lp->flags); if (test_bit(DFL_BLOCK_LOCKS, &ls->flags) && !test_bit(LFL_NOBLOCK, &lp->flags)) gdlm_queue_delayed(lp); else queue_submit(lp); return; } /* * A request is granted during dlm recovery. It may be granted * because the locks of a failed node were cleared. In that case, * there may be inconsistent data beneath this lock and we must wait * for recovery to complete to use it. When gfs recovery is done this * granted lock will be converted to NL and then reacquired in this * granted state. */ if (test_bit(DFL_BLOCK_LOCKS, &ls->flags) && !test_bit(LFL_NOBLOCK, &lp->flags) && lp->req != DLM_LOCK_NL) { lp->cur = lp->req; lp->prev_req = lp->req; lp->req = DLM_LOCK_NL; lp->lkf |= DLM_LKF_CONVERT; lp->lkf &= ~DLM_LKF_CONVDEADLK; log_debug("rereq %x,%llx id %x %d,%d", lp->lockname.ln_type, (unsigned long long)lp->lockname.ln_number, lp->lksb.sb_lkid, lp->cur, lp->req); set_bit(LFL_REREQUEST, &lp->flags); queue_submit(lp); return; } /* * DLM demoted the lock to NL before it was granted so GFS must be * told it cannot cache data for this lock. */ if (lp->lksb.sb_flags & DLM_SBF_DEMOTED) set_bit(LFL_NOCACHE, &lp->flags); out: /* * This is an internal lock_dlm lock */ if (test_bit(LFL_INLOCK, &lp->flags)) { clear_bit(LFL_NOBLOCK, &lp->flags); lp->cur = lp->req; complete(&lp->ast_wait); return; } /* * Normal completion of a lock request. Tell GFS it now has the lock. */ clear_bit(LFL_NOBLOCK, &lp->flags); lp->cur = lp->req; acb.lc_name = lp->lockname; acb.lc_ret |= gdlm_make_lmstate(lp->cur); if (!test_and_clear_bit(LFL_NOCACHE, &lp->flags) && (lp->cur > DLM_LOCK_NL) && (prev_mode > DLM_LOCK_NL)) acb.lc_ret |= LM_OUT_CACHEABLE; ls->fscb(ls->sdp, LM_CB_ASYNC, &acb); } static inline int no_work(struct gdlm_ls *ls, int blocking) { int ret; spin_lock(&ls->async_lock); ret = list_empty(&ls->complete) && list_empty(&ls->submit); if (ret && blocking) ret = list_empty(&ls->blocking); spin_unlock(&ls->async_lock); return ret; } static inline int check_drop(struct gdlm_ls *ls) { if (!ls->drop_locks_count) return 0; if (time_after(jiffies, ls->drop_time + ls->drop_locks_period * HZ)) { ls->drop_time = jiffies; if (ls->all_locks_count >= ls->drop_locks_count) return 1; } return 0; } static int gdlm_thread(void *data) { struct gdlm_ls *ls = (struct gdlm_ls *) data; struct gdlm_lock *lp = NULL; int blist = 0; uint8_t complete, blocking, submit, drop; DECLARE_WAITQUEUE(wait, current); /* Only thread1 is allowed to do blocking callbacks since gfs may wait for a completion callback within a blocking cb. */ if (current == ls->thread1) blist = 1; while (!kthread_should_stop()) { set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&ls->thread_wait, &wait); if (no_work(ls, blist)) schedule(); remove_wait_queue(&ls->thread_wait, &wait); set_current_state(TASK_RUNNING); complete = blocking = submit = drop = 0; spin_lock(&ls->async_lock); if (blist && !list_empty(&ls->blocking)) { lp = list_entry(ls->blocking.next, struct gdlm_lock, blist); list_del_init(&lp->blist); blocking = lp->bast_mode; lp->bast_mode = 0; } else if (!list_empty(&ls->complete)) { lp = list_entry(ls->complete.next, struct gdlm_lock, clist); list_del_init(&lp->clist); complete = 1; } else if (!list_empty(&ls->submit)) { lp = list_entry(ls->submit.next, struct gdlm_lock, delay_list); list_del_init(&lp->delay_list); submit = 1; } drop = check_drop(ls); spin_unlock(&ls->async_lock); if (complete) process_complete(lp); else if (blocking) process_blocking(lp, blocking); else if (submit) gdlm_do_lock(lp); if (drop) ls->fscb(ls->sdp, LM_CB_DROPLOCKS, NULL); schedule(); } return 0; } int gdlm_init_threads(struct gdlm_ls *ls) { struct task_struct *p; int error; p = kthread_run(gdlm_thread, ls, "lock_dlm1"); error = IS_ERR(p); if (error) { log_error("can't start lock_dlm1 thread %d", error); return error; } ls->thread1 = p; p = kthread_run(gdlm_thread, ls, "lock_dlm2"); error = IS_ERR(p); if (error) { log_error("can't start lock_dlm2 thread %d", error); kthread_stop(ls->thread1); return error; } ls->thread2 = p; return 0; } void gdlm_release_threads(struct gdlm_ls *ls) { kthread_stop(ls->thread1); kthread_stop(ls->thread2); }