brintos

brintos / linux-shallow public Read only

0
0
Text · 6.9 KiB · 742b30b Raw
276 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/******************************************************************************3*******************************************************************************4**5**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.6**  Copyright (C) 2004-2010 Red Hat, Inc.  All rights reserved.7**8**9*******************************************************************************10******************************************************************************/11 12#include <trace/events/dlm.h>13 14#include "dlm_internal.h"15#include "lvb_table.h"16#include "memory.h"17#include "lock.h"18#include "user.h"19#include "ast.h"20 21static void dlm_run_callback(uint32_t ls_id, uint32_t lkb_id, int8_t mode,22			     uint32_t flags, uint8_t sb_flags, int sb_status,23			     struct dlm_lksb *lksb,24			     void (*astfn)(void *astparam),25			     void (*bastfn)(void *astparam, int mode),26			     void *astparam, const char *res_name,27			     size_t res_length)28{29	if (flags & DLM_CB_BAST) {30		trace_dlm_bast(ls_id, lkb_id, mode, res_name, res_length);31		bastfn(astparam, mode);32	} else if (flags & DLM_CB_CAST) {33		trace_dlm_ast(ls_id, lkb_id, sb_status, sb_flags, res_name,34			      res_length);35		lksb->sb_status = sb_status;36		lksb->sb_flags = sb_flags;37		astfn(astparam);38	}39}40 41static void dlm_do_callback(struct dlm_callback *cb)42{43	dlm_run_callback(cb->ls_id, cb->lkb_id, cb->mode, cb->flags,44			 cb->sb_flags, cb->sb_status, cb->lkb_lksb,45			 cb->astfn, cb->bastfn, cb->astparam,46			 cb->res_name, cb->res_length);47	dlm_free_cb(cb);48}49 50static void dlm_callback_work(struct work_struct *work)51{52	struct dlm_callback *cb = container_of(work, struct dlm_callback, work);53 54	dlm_do_callback(cb);55}56 57bool dlm_may_skip_callback(struct dlm_lkb *lkb, uint32_t flags, int mode,58			   int status, uint32_t sbflags, int *copy_lvb)59{60	struct dlm_rsb *rsb = lkb->lkb_resource;61	struct dlm_ls *ls = rsb->res_ls;62	int prev_mode;63 64	if (copy_lvb)65		*copy_lvb = 0;66 67	if (flags & DLM_CB_BAST) {68		/* if cb is a bast, it should be skipped if the blocking mode is69		 * compatible with the last granted mode70		 */71		if (lkb->lkb_last_cast_cb_mode != -1) {72			if (dlm_modes_compat(mode, lkb->lkb_last_cast_cb_mode)) {73				log_debug(ls, "skip %x bast mode %d for cast mode %d",74					  lkb->lkb_id, mode,75					  lkb->lkb_last_cast_cb_mode);76				return true;77			}78		}79 80		/*81		 * Suppress some redundant basts here, do more on removal.82		 * Don't even add a bast if the callback just before it83		 * is a bast for the same mode or a more restrictive mode.84		 * (the addional > PR check is needed for PR/CW inversion)85		 */86		if (lkb->lkb_last_cb_mode != -1 &&87		    lkb->lkb_last_cb_flags & DLM_CB_BAST) {88			prev_mode = lkb->lkb_last_cb_mode;89 90			if ((prev_mode == mode) ||91			    (prev_mode > mode && prev_mode > DLM_LOCK_PR)) {92				log_debug(ls, "skip %x add bast mode %d for bast mode %d",93					  lkb->lkb_id, mode, prev_mode);94				return true;95			}96		}97 98		lkb->lkb_last_bast_time = ktime_get();99		lkb->lkb_last_bast_cb_mode = mode;100	} else if (flags & DLM_CB_CAST) {101		if (test_bit(DLM_DFL_USER_BIT, &lkb->lkb_dflags)) {102			prev_mode = lkb->lkb_last_cast_cb_mode;103 104			if (!status && lkb->lkb_lksb->sb_lvbptr &&105			    dlm_lvb_operations[prev_mode + 1][mode + 1]) {106				if (copy_lvb)107					*copy_lvb = 1;108			}109		}110 111		lkb->lkb_last_cast_cb_mode = mode;112		lkb->lkb_last_cast_time = ktime_get();113	}114 115	lkb->lkb_last_cb_mode = mode;116	lkb->lkb_last_cb_flags = flags;117 118	return false;119}120 121int dlm_get_cb(struct dlm_lkb *lkb, uint32_t flags, int mode,122	       int status, uint32_t sbflags,123	       struct dlm_callback **cb)124{125	struct dlm_rsb *rsb = lkb->lkb_resource;126	struct dlm_ls *ls = rsb->res_ls;127 128	*cb = dlm_allocate_cb();129	if (WARN_ON_ONCE(!*cb))130		return -ENOMEM;131 132	/* for tracing */133	(*cb)->lkb_id = lkb->lkb_id;134	(*cb)->ls_id = ls->ls_global_id;135	memcpy((*cb)->res_name, rsb->res_name, rsb->res_length);136	(*cb)->res_length = rsb->res_length;137 138	(*cb)->flags = flags;139	(*cb)->mode = mode;140	(*cb)->sb_status = status;141	(*cb)->sb_flags = (sbflags & 0x000000FF);142	(*cb)->lkb_lksb = lkb->lkb_lksb;143 144	return 0;145}146 147static int dlm_get_queue_cb(struct dlm_lkb *lkb, uint32_t flags, int mode,148			    int status, uint32_t sbflags,149			    struct dlm_callback **cb)150{151	int rv;152 153	rv = dlm_get_cb(lkb, flags, mode, status, sbflags, cb);154	if (rv)155		return rv;156 157	(*cb)->astfn = lkb->lkb_astfn;158	(*cb)->bastfn = lkb->lkb_bastfn;159	(*cb)->astparam = lkb->lkb_astparam;160	INIT_WORK(&(*cb)->work, dlm_callback_work);161 162	return 0;163}164 165void dlm_add_cb(struct dlm_lkb *lkb, uint32_t flags, int mode, int status,166		uint32_t sbflags)167{168	struct dlm_rsb *rsb = lkb->lkb_resource;169	struct dlm_ls *ls = rsb->res_ls;170	struct dlm_callback *cb;171	int rv;172 173	if (test_bit(DLM_DFL_USER_BIT, &lkb->lkb_dflags)) {174		dlm_user_add_ast(lkb, flags, mode, status, sbflags);175		return;176	}177 178	if (dlm_may_skip_callback(lkb, flags, mode, status, sbflags, NULL))179		return;180 181	spin_lock_bh(&ls->ls_cb_lock);182	if (test_bit(LSFL_CB_DELAY, &ls->ls_flags)) {183		rv = dlm_get_queue_cb(lkb, flags, mode, status, sbflags, &cb);184		if (!rv)185			list_add(&cb->list, &ls->ls_cb_delay);186	} else {187		if (test_bit(LSFL_SOFTIRQ, &ls->ls_flags)) {188			dlm_run_callback(ls->ls_global_id, lkb->lkb_id, mode, flags,189					 sbflags, status, lkb->lkb_lksb,190					 lkb->lkb_astfn, lkb->lkb_bastfn,191					 lkb->lkb_astparam, rsb->res_name,192					 rsb->res_length);193		} else {194			rv = dlm_get_queue_cb(lkb, flags, mode, status, sbflags, &cb);195			if (!rv)196				queue_work(ls->ls_callback_wq, &cb->work);197		}198	}199	spin_unlock_bh(&ls->ls_cb_lock);200}201 202int dlm_callback_start(struct dlm_ls *ls)203{204	if (!test_bit(LSFL_FS, &ls->ls_flags) ||205	    test_bit(LSFL_SOFTIRQ, &ls->ls_flags))206		return 0;207 208	ls->ls_callback_wq = alloc_ordered_workqueue("dlm_callback",209						     WQ_HIGHPRI | WQ_MEM_RECLAIM);210	if (!ls->ls_callback_wq) {211		log_print("can't start dlm_callback workqueue");212		return -ENOMEM;213	}214	return 0;215}216 217void dlm_callback_stop(struct dlm_ls *ls)218{219	if (ls->ls_callback_wq)220		destroy_workqueue(ls->ls_callback_wq);221}222 223void dlm_callback_suspend(struct dlm_ls *ls)224{225	if (!test_bit(LSFL_FS, &ls->ls_flags))226		return;227 228	spin_lock_bh(&ls->ls_cb_lock);229	set_bit(LSFL_CB_DELAY, &ls->ls_flags);230	spin_unlock_bh(&ls->ls_cb_lock);231 232	if (ls->ls_callback_wq)233		flush_workqueue(ls->ls_callback_wq);234}235 236#define MAX_CB_QUEUE 25237 238void dlm_callback_resume(struct dlm_ls *ls)239{240	struct dlm_callback *cb, *safe;241	int count = 0, sum = 0;242	bool empty;243 244	if (!test_bit(LSFL_FS, &ls->ls_flags))245		return;246 247more:248	spin_lock_bh(&ls->ls_cb_lock);249	list_for_each_entry_safe(cb, safe, &ls->ls_cb_delay, list) {250		list_del(&cb->list);251		if (test_bit(LSFL_SOFTIRQ, &ls->ls_flags))252			dlm_do_callback(cb);253		else254			queue_work(ls->ls_callback_wq, &cb->work);255 256		count++;257		if (count == MAX_CB_QUEUE)258			break;259	}260	empty = list_empty(&ls->ls_cb_delay);261	if (empty)262		clear_bit(LSFL_CB_DELAY, &ls->ls_flags);263	spin_unlock_bh(&ls->ls_cb_lock);264 265	sum += count;266	if (!empty) {267		count = 0;268		cond_resched();269		goto more;270	}271 272	if (sum)273		log_rinfo(ls, "%s %d", __func__, sum);274}275 276