162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/****************************************************************************** 362306a36Sopenharmony_ci******************************************************************************* 462306a36Sopenharmony_ci** 562306a36Sopenharmony_ci** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. 662306a36Sopenharmony_ci** Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved. 762306a36Sopenharmony_ci** 862306a36Sopenharmony_ci** 962306a36Sopenharmony_ci******************************************************************************* 1062306a36Sopenharmony_ci******************************************************************************/ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <trace/events/dlm.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "dlm_internal.h" 1562306a36Sopenharmony_ci#include "memory.h" 1662306a36Sopenharmony_ci#include "lock.h" 1762306a36Sopenharmony_ci#include "user.h" 1862306a36Sopenharmony_ci#include "ast.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_civoid dlm_release_callback(struct kref *ref) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci struct dlm_callback *cb = container_of(ref, struct dlm_callback, ref); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci dlm_free_cb(cb); 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_civoid dlm_callback_set_last_ptr(struct dlm_callback **from, 2862306a36Sopenharmony_ci struct dlm_callback *to) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci if (*from) 3162306a36Sopenharmony_ci kref_put(&(*from)->ref, dlm_release_callback); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci if (to) 3462306a36Sopenharmony_ci kref_get(&to->ref); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci *from = to; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ciint dlm_enqueue_lkb_callback(struct dlm_lkb *lkb, uint32_t flags, int mode, 4062306a36Sopenharmony_ci int status, uint32_t sbflags) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci struct dlm_ls *ls = lkb->lkb_resource->res_ls; 4362306a36Sopenharmony_ci int rv = DLM_ENQUEUE_CALLBACK_SUCCESS; 4462306a36Sopenharmony_ci struct dlm_callback *cb; 4562306a36Sopenharmony_ci int prev_mode; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if (flags & DLM_CB_BAST) { 4862306a36Sopenharmony_ci /* if cb is a bast, it should be skipped if the blocking mode is 4962306a36Sopenharmony_ci * compatible with the last granted mode 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci if (lkb->lkb_last_cast) { 5262306a36Sopenharmony_ci if (dlm_modes_compat(mode, lkb->lkb_last_cast->mode)) { 5362306a36Sopenharmony_ci log_debug(ls, "skip %x bast mode %d for cast mode %d", 5462306a36Sopenharmony_ci lkb->lkb_id, mode, 5562306a36Sopenharmony_ci lkb->lkb_last_cast->mode); 5662306a36Sopenharmony_ci goto out; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* 6162306a36Sopenharmony_ci * Suppress some redundant basts here, do more on removal. 6262306a36Sopenharmony_ci * Don't even add a bast if the callback just before it 6362306a36Sopenharmony_ci * is a bast for the same mode or a more restrictive mode. 6462306a36Sopenharmony_ci * (the addional > PR check is needed for PR/CW inversion) 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_ci if (lkb->lkb_last_cb && lkb->lkb_last_cb->flags & DLM_CB_BAST) { 6762306a36Sopenharmony_ci prev_mode = lkb->lkb_last_cb->mode; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if ((prev_mode == mode) || 7062306a36Sopenharmony_ci (prev_mode > mode && prev_mode > DLM_LOCK_PR)) { 7162306a36Sopenharmony_ci log_debug(ls, "skip %x add bast mode %d for bast mode %d", 7262306a36Sopenharmony_ci lkb->lkb_id, mode, prev_mode); 7362306a36Sopenharmony_ci goto out; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci cb = dlm_allocate_cb(); 7962306a36Sopenharmony_ci if (!cb) { 8062306a36Sopenharmony_ci rv = DLM_ENQUEUE_CALLBACK_FAILURE; 8162306a36Sopenharmony_ci goto out; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci cb->flags = flags; 8562306a36Sopenharmony_ci cb->mode = mode; 8662306a36Sopenharmony_ci cb->sb_status = status; 8762306a36Sopenharmony_ci cb->sb_flags = (sbflags & 0x000000FF); 8862306a36Sopenharmony_ci kref_init(&cb->ref); 8962306a36Sopenharmony_ci if (!test_and_set_bit(DLM_IFL_CB_PENDING_BIT, &lkb->lkb_iflags)) 9062306a36Sopenharmony_ci rv = DLM_ENQUEUE_CALLBACK_NEED_SCHED; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci list_add_tail(&cb->list, &lkb->lkb_callbacks); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (flags & DLM_CB_CAST) 9562306a36Sopenharmony_ci dlm_callback_set_last_ptr(&lkb->lkb_last_cast, cb); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci dlm_callback_set_last_ptr(&lkb->lkb_last_cb, cb); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci out: 10062306a36Sopenharmony_ci return rv; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ciint dlm_dequeue_lkb_callback(struct dlm_lkb *lkb, struct dlm_callback **cb) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci /* oldest undelivered cb is callbacks first entry */ 10662306a36Sopenharmony_ci *cb = list_first_entry_or_null(&lkb->lkb_callbacks, 10762306a36Sopenharmony_ci struct dlm_callback, list); 10862306a36Sopenharmony_ci if (!*cb) 10962306a36Sopenharmony_ci return DLM_DEQUEUE_CALLBACK_EMPTY; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* remove it from callbacks so shift others down */ 11262306a36Sopenharmony_ci list_del(&(*cb)->list); 11362306a36Sopenharmony_ci if (list_empty(&lkb->lkb_callbacks)) 11462306a36Sopenharmony_ci return DLM_DEQUEUE_CALLBACK_LAST; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return DLM_DEQUEUE_CALLBACK_SUCCESS; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_civoid dlm_add_cb(struct dlm_lkb *lkb, uint32_t flags, int mode, int status, 12062306a36Sopenharmony_ci uint32_t sbflags) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct dlm_ls *ls = lkb->lkb_resource->res_ls; 12362306a36Sopenharmony_ci int rv; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (test_bit(DLM_DFL_USER_BIT, &lkb->lkb_dflags)) { 12662306a36Sopenharmony_ci dlm_user_add_ast(lkb, flags, mode, status, sbflags); 12762306a36Sopenharmony_ci return; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci spin_lock(&lkb->lkb_cb_lock); 13162306a36Sopenharmony_ci rv = dlm_enqueue_lkb_callback(lkb, flags, mode, status, sbflags); 13262306a36Sopenharmony_ci switch (rv) { 13362306a36Sopenharmony_ci case DLM_ENQUEUE_CALLBACK_NEED_SCHED: 13462306a36Sopenharmony_ci kref_get(&lkb->lkb_ref); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci spin_lock(&ls->ls_cb_lock); 13762306a36Sopenharmony_ci if (test_bit(LSFL_CB_DELAY, &ls->ls_flags)) { 13862306a36Sopenharmony_ci list_add(&lkb->lkb_cb_list, &ls->ls_cb_delay); 13962306a36Sopenharmony_ci } else { 14062306a36Sopenharmony_ci queue_work(ls->ls_callback_wq, &lkb->lkb_cb_work); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci spin_unlock(&ls->ls_cb_lock); 14362306a36Sopenharmony_ci break; 14462306a36Sopenharmony_ci case DLM_ENQUEUE_CALLBACK_FAILURE: 14562306a36Sopenharmony_ci WARN_ON_ONCE(1); 14662306a36Sopenharmony_ci break; 14762306a36Sopenharmony_ci case DLM_ENQUEUE_CALLBACK_SUCCESS: 14862306a36Sopenharmony_ci break; 14962306a36Sopenharmony_ci default: 15062306a36Sopenharmony_ci WARN_ON_ONCE(1); 15162306a36Sopenharmony_ci break; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci spin_unlock(&lkb->lkb_cb_lock); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_civoid dlm_callback_work(struct work_struct *work) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct dlm_lkb *lkb = container_of(work, struct dlm_lkb, lkb_cb_work); 15962306a36Sopenharmony_ci struct dlm_ls *ls = lkb->lkb_resource->res_ls; 16062306a36Sopenharmony_ci void (*castfn) (void *astparam); 16162306a36Sopenharmony_ci void (*bastfn) (void *astparam, int mode); 16262306a36Sopenharmony_ci struct dlm_callback *cb; 16362306a36Sopenharmony_ci int rv; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci spin_lock(&lkb->lkb_cb_lock); 16662306a36Sopenharmony_ci rv = dlm_dequeue_lkb_callback(lkb, &cb); 16762306a36Sopenharmony_ci if (WARN_ON_ONCE(rv == DLM_DEQUEUE_CALLBACK_EMPTY)) { 16862306a36Sopenharmony_ci clear_bit(DLM_IFL_CB_PENDING_BIT, &lkb->lkb_iflags); 16962306a36Sopenharmony_ci spin_unlock(&lkb->lkb_cb_lock); 17062306a36Sopenharmony_ci goto out; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci spin_unlock(&lkb->lkb_cb_lock); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci for (;;) { 17562306a36Sopenharmony_ci castfn = lkb->lkb_astfn; 17662306a36Sopenharmony_ci bastfn = lkb->lkb_bastfn; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (cb->flags & DLM_CB_BAST) { 17962306a36Sopenharmony_ci trace_dlm_bast(ls, lkb, cb->mode); 18062306a36Sopenharmony_ci lkb->lkb_last_bast_time = ktime_get(); 18162306a36Sopenharmony_ci lkb->lkb_last_bast_mode = cb->mode; 18262306a36Sopenharmony_ci bastfn(lkb->lkb_astparam, cb->mode); 18362306a36Sopenharmony_ci } else if (cb->flags & DLM_CB_CAST) { 18462306a36Sopenharmony_ci lkb->lkb_lksb->sb_status = cb->sb_status; 18562306a36Sopenharmony_ci lkb->lkb_lksb->sb_flags = cb->sb_flags; 18662306a36Sopenharmony_ci trace_dlm_ast(ls, lkb); 18762306a36Sopenharmony_ci lkb->lkb_last_cast_time = ktime_get(); 18862306a36Sopenharmony_ci castfn(lkb->lkb_astparam); 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci kref_put(&cb->ref, dlm_release_callback); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci spin_lock(&lkb->lkb_cb_lock); 19462306a36Sopenharmony_ci rv = dlm_dequeue_lkb_callback(lkb, &cb); 19562306a36Sopenharmony_ci if (rv == DLM_DEQUEUE_CALLBACK_EMPTY) { 19662306a36Sopenharmony_ci clear_bit(DLM_IFL_CB_PENDING_BIT, &lkb->lkb_iflags); 19762306a36Sopenharmony_ci spin_unlock(&lkb->lkb_cb_lock); 19862306a36Sopenharmony_ci break; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci spin_unlock(&lkb->lkb_cb_lock); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ciout: 20462306a36Sopenharmony_ci /* undo kref_get from dlm_add_callback, may cause lkb to be freed */ 20562306a36Sopenharmony_ci dlm_put_lkb(lkb); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ciint dlm_callback_start(struct dlm_ls *ls) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci ls->ls_callback_wq = alloc_workqueue("dlm_callback", 21162306a36Sopenharmony_ci WQ_HIGHPRI | WQ_MEM_RECLAIM, 0); 21262306a36Sopenharmony_ci if (!ls->ls_callback_wq) { 21362306a36Sopenharmony_ci log_print("can't start dlm_callback workqueue"); 21462306a36Sopenharmony_ci return -ENOMEM; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_civoid dlm_callback_stop(struct dlm_ls *ls) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci if (ls->ls_callback_wq) 22262306a36Sopenharmony_ci destroy_workqueue(ls->ls_callback_wq); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_civoid dlm_callback_suspend(struct dlm_ls *ls) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci if (ls->ls_callback_wq) { 22862306a36Sopenharmony_ci spin_lock(&ls->ls_cb_lock); 22962306a36Sopenharmony_ci set_bit(LSFL_CB_DELAY, &ls->ls_flags); 23062306a36Sopenharmony_ci spin_unlock(&ls->ls_cb_lock); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci flush_workqueue(ls->ls_callback_wq); 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci#define MAX_CB_QUEUE 25 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_civoid dlm_callback_resume(struct dlm_ls *ls) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct dlm_lkb *lkb, *safe; 24162306a36Sopenharmony_ci int count = 0, sum = 0; 24262306a36Sopenharmony_ci bool empty; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (!ls->ls_callback_wq) 24562306a36Sopenharmony_ci return; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cimore: 24862306a36Sopenharmony_ci spin_lock(&ls->ls_cb_lock); 24962306a36Sopenharmony_ci list_for_each_entry_safe(lkb, safe, &ls->ls_cb_delay, lkb_cb_list) { 25062306a36Sopenharmony_ci list_del_init(&lkb->lkb_cb_list); 25162306a36Sopenharmony_ci queue_work(ls->ls_callback_wq, &lkb->lkb_cb_work); 25262306a36Sopenharmony_ci count++; 25362306a36Sopenharmony_ci if (count == MAX_CB_QUEUE) 25462306a36Sopenharmony_ci break; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci empty = list_empty(&ls->ls_cb_delay); 25762306a36Sopenharmony_ci if (empty) 25862306a36Sopenharmony_ci clear_bit(LSFL_CB_DELAY, &ls->ls_flags); 25962306a36Sopenharmony_ci spin_unlock(&ls->ls_cb_lock); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci sum += count; 26262306a36Sopenharmony_ci if (!empty) { 26362306a36Sopenharmony_ci count = 0; 26462306a36Sopenharmony_ci cond_resched(); 26562306a36Sopenharmony_ci goto more; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (sum) 26962306a36Sopenharmony_ci log_rinfo(ls, "%s %d", __func__, sum); 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 272