diff options
Diffstat (limited to 'fs/dlm/ast.c')
| -rw-r--r-- | fs/dlm/ast.c | 393 | 
1 files changed, 250 insertions, 143 deletions
diff --git a/fs/dlm/ast.c b/fs/dlm/ast.c index 4314f0d48d8..dcea1e37a1b 100644 --- a/fs/dlm/ast.c +++ b/fs/dlm/ast.c @@ -16,192 +16,299 @@  #include "user.h"  #include "ast.h" -#define WAKE_ASTS  0 +static uint64_t dlm_cb_seq; +static DEFINE_SPINLOCK(dlm_cb_seq_spin); -static struct list_head		ast_queue; -static spinlock_t		ast_queue_lock; -static struct task_struct *	astd_task; -static unsigned long		astd_wakeflags; -static struct mutex		astd_running; - - -void dlm_del_ast(struct dlm_lkb *lkb) +static void dlm_dump_lkb_callbacks(struct dlm_lkb *lkb)  { -	spin_lock(&ast_queue_lock); -	if (lkb->lkb_ast_type & (AST_COMP | AST_BAST)) -		list_del(&lkb->lkb_astqueue); -	spin_unlock(&ast_queue_lock); +	int i; + +	log_print("last_bast %x %llu flags %x mode %d sb %d %x", +		  lkb->lkb_id, +		  (unsigned long long)lkb->lkb_last_bast.seq, +		  lkb->lkb_last_bast.flags, +		  lkb->lkb_last_bast.mode, +		  lkb->lkb_last_bast.sb_status, +		  lkb->lkb_last_bast.sb_flags); + +	log_print("last_cast %x %llu flags %x mode %d sb %d %x", +		  lkb->lkb_id, +		  (unsigned long long)lkb->lkb_last_cast.seq, +		  lkb->lkb_last_cast.flags, +		  lkb->lkb_last_cast.mode, +		  lkb->lkb_last_cast.sb_status, +		  lkb->lkb_last_cast.sb_flags); + +	for (i = 0; i < DLM_CALLBACKS_SIZE; i++) { +		log_print("cb %x %llu flags %x mode %d sb %d %x", +			  lkb->lkb_id, +			  (unsigned long long)lkb->lkb_callbacks[i].seq, +			  lkb->lkb_callbacks[i].flags, +			  lkb->lkb_callbacks[i].mode, +			  lkb->lkb_callbacks[i].sb_status, +			  lkb->lkb_callbacks[i].sb_flags); +	}  } -void dlm_add_ast(struct dlm_lkb *lkb, int type, int mode) +int dlm_add_lkb_callback(struct dlm_lkb *lkb, uint32_t flags, int mode, +			 int status, uint32_t sbflags, uint64_t seq)  { -	if (lkb->lkb_flags & DLM_IFL_USER) { -		dlm_user_add_ast(lkb, type, mode); -		return; +	struct dlm_ls *ls = lkb->lkb_resource->res_ls; +	uint64_t prev_seq; +	int prev_mode; +	int i, rv; + +	for (i = 0; i < DLM_CALLBACKS_SIZE; i++) { +		if (lkb->lkb_callbacks[i].seq) +			continue; + +		/* +		 * Suppress some redundant basts here, do more on removal. +		 * Don't even add a bast if the callback just before it +		 * is a bast for the same mode or a more restrictive mode. +		 * (the addional > PR check is needed for PR/CW inversion) +		 */ + +		if ((i > 0) && (flags & DLM_CB_BAST) && +		    (lkb->lkb_callbacks[i-1].flags & DLM_CB_BAST)) { + +			prev_seq = lkb->lkb_callbacks[i-1].seq; +			prev_mode = lkb->lkb_callbacks[i-1].mode; + +			if ((prev_mode == mode) || +			    (prev_mode > mode && prev_mode > DLM_LOCK_PR)) { + +				log_debug(ls, "skip %x add bast %llu mode %d " +					  "for bast %llu mode %d", +					  lkb->lkb_id, +					  (unsigned long long)seq, +					  mode, +					  (unsigned long long)prev_seq, +					  prev_mode); +				rv = 0; +				goto out; +			} +		} + +		lkb->lkb_callbacks[i].seq = seq; +		lkb->lkb_callbacks[i].flags = flags; +		lkb->lkb_callbacks[i].mode = mode; +		lkb->lkb_callbacks[i].sb_status = status; +		lkb->lkb_callbacks[i].sb_flags = (sbflags & 0x000000FF); +		rv = 0; +		break;  	} -	spin_lock(&ast_queue_lock); -	if (!(lkb->lkb_ast_type & (AST_COMP | AST_BAST))) { -		kref_get(&lkb->lkb_ref); -		list_add_tail(&lkb->lkb_astqueue, &ast_queue); -		lkb->lkb_ast_first = type; +	if (i == DLM_CALLBACKS_SIZE) { +		log_error(ls, "no callbacks %x %llu flags %x mode %d sb %d %x", +			  lkb->lkb_id, (unsigned long long)seq, +			  flags, mode, status, sbflags); +		dlm_dump_lkb_callbacks(lkb); +		rv = -1; +		goto out; +	} + out: +	return rv; +} + +int dlm_rem_lkb_callback(struct dlm_ls *ls, struct dlm_lkb *lkb, +			 struct dlm_callback *cb, int *resid) +{ +	int i, rv; + +	*resid = 0; + +	if (!lkb->lkb_callbacks[0].seq) { +		rv = -ENOENT; +		goto out;  	} -	/* sanity check, this should not happen */ +	/* oldest undelivered cb is callbacks[0] */ + +	memcpy(cb, &lkb->lkb_callbacks[0], sizeof(struct dlm_callback)); +	memset(&lkb->lkb_callbacks[0], 0, sizeof(struct dlm_callback)); + +	/* shift others down */ -	if ((type == AST_COMP) && (lkb->lkb_ast_type & AST_COMP)) -		log_print("repeat cast %d castmode %d lock %x %s", -			  mode, lkb->lkb_castmode, -			  lkb->lkb_id, lkb->lkb_resource->res_name); +	for (i = 1; i < DLM_CALLBACKS_SIZE; i++) { +		if (!lkb->lkb_callbacks[i].seq) +			break; +		memcpy(&lkb->lkb_callbacks[i-1], &lkb->lkb_callbacks[i], +		       sizeof(struct dlm_callback)); +		memset(&lkb->lkb_callbacks[i], 0, sizeof(struct dlm_callback)); +		(*resid)++; +	} -	lkb->lkb_ast_type |= type; -	if (type == AST_BAST) -		lkb->lkb_bastmode = mode; -	else -		lkb->lkb_castmode = mode; -	spin_unlock(&ast_queue_lock); +	/* if cb is a bast, it should be skipped if the blocking mode is +	   compatible with the last granted mode */ + +	if ((cb->flags & DLM_CB_BAST) && lkb->lkb_last_cast.seq) { +		if (dlm_modes_compat(cb->mode, lkb->lkb_last_cast.mode)) { +			cb->flags |= DLM_CB_SKIP; + +			log_debug(ls, "skip %x bast %llu mode %d " +				  "for cast %llu mode %d", +				  lkb->lkb_id, +				  (unsigned long long)cb->seq, +				  cb->mode, +				  (unsigned long long)lkb->lkb_last_cast.seq, +				  lkb->lkb_last_cast.mode); +			rv = 0; +			goto out; +		} +	} -	set_bit(WAKE_ASTS, &astd_wakeflags); -	wake_up_process(astd_task); +	if (cb->flags & DLM_CB_CAST) { +		memcpy(&lkb->lkb_last_cast, cb, sizeof(struct dlm_callback)); +		lkb->lkb_last_cast_time = ktime_get(); +	} + +	if (cb->flags & DLM_CB_BAST) { +		memcpy(&lkb->lkb_last_bast, cb, sizeof(struct dlm_callback)); +		lkb->lkb_last_bast_time = ktime_get(); +	} +	rv = 0; + out: +	return rv;  } -static void process_asts(void) +void dlm_add_cb(struct dlm_lkb *lkb, uint32_t flags, int mode, int status, +		uint32_t sbflags)  { -	struct dlm_ls *ls = NULL; -	struct dlm_rsb *r = NULL; -	struct dlm_lkb *lkb; -	void (*castfn) (void *astparam); -	void (*bastfn) (void *astparam, int mode); -	int type, first, bastmode, castmode, do_bast, do_cast, last_castmode; +	struct dlm_ls *ls = lkb->lkb_resource->res_ls; +	uint64_t new_seq, prev_seq; +	int rv; -repeat: -	spin_lock(&ast_queue_lock); -	list_for_each_entry(lkb, &ast_queue, lkb_astqueue) { -		r = lkb->lkb_resource; -		ls = r->res_ls; +	spin_lock(&dlm_cb_seq_spin); +	new_seq = ++dlm_cb_seq; +	spin_unlock(&dlm_cb_seq_spin); -		if (dlm_locking_stopped(ls)) -			continue; +	if (lkb->lkb_flags & DLM_IFL_USER) { +		dlm_user_add_ast(lkb, flags, mode, status, sbflags, new_seq); +		return; +	} -		list_del(&lkb->lkb_astqueue); -		type = lkb->lkb_ast_type; -		lkb->lkb_ast_type = 0; -		first = lkb->lkb_ast_first; -		lkb->lkb_ast_first = 0; -		bastmode = lkb->lkb_bastmode; -		castmode = lkb->lkb_castmode; -		castfn = lkb->lkb_astfn; -		bastfn = lkb->lkb_bastfn; -		spin_unlock(&ast_queue_lock); - -		do_cast = (type & AST_COMP) && castfn; -		do_bast = (type & AST_BAST) && bastfn; - -		/* Skip a bast if its blocking mode is compatible with the -		   granted mode of the preceding cast. */ - -		if (do_bast) { -			if (first == AST_COMP) -				last_castmode = castmode; -			else -				last_castmode = lkb->lkb_castmode_done; -			if (dlm_modes_compat(bastmode, last_castmode)) -				do_bast = 0; -		} +	mutex_lock(&lkb->lkb_cb_mutex); +	prev_seq = lkb->lkb_callbacks[0].seq; + +	rv = dlm_add_lkb_callback(lkb, flags, mode, status, sbflags, new_seq); +	if (rv < 0) +		goto out; + +	if (!prev_seq) { +		kref_get(&lkb->lkb_ref); -		if (first == AST_COMP) { -			if (do_cast) -				castfn(lkb->lkb_astparam); -			if (do_bast) -				bastfn(lkb->lkb_astparam, bastmode); -		} else if (first == AST_BAST) { -			if (do_bast) -				bastfn(lkb->lkb_astparam, bastmode); -			if (do_cast) -				castfn(lkb->lkb_astparam); +		if (test_bit(LSFL_CB_DELAY, &ls->ls_flags)) { +			mutex_lock(&ls->ls_cb_mutex); +			list_add(&lkb->lkb_cb_list, &ls->ls_cb_delay); +			mutex_unlock(&ls->ls_cb_mutex);  		} else { -			log_error(ls, "bad ast_first %d ast_type %d", -				  first, type); +			queue_work(ls->ls_callback_wq, &lkb->lkb_cb_work);  		} +	} + out: +	mutex_unlock(&lkb->lkb_cb_mutex); +} -		if (do_cast) -			lkb->lkb_castmode_done = castmode; -		if (do_bast) -			lkb->lkb_bastmode_done = bastmode; +void dlm_callback_work(struct work_struct *work) +{ +	struct dlm_lkb *lkb = container_of(work, struct dlm_lkb, lkb_cb_work); +	struct dlm_ls *ls = lkb->lkb_resource->res_ls; +	void (*castfn) (void *astparam); +	void (*bastfn) (void *astparam, int mode); +	struct dlm_callback callbacks[DLM_CALLBACKS_SIZE]; +	int i, rv, resid; -		/* this removes the reference added by dlm_add_ast -		   and may result in the lkb being freed */ -		dlm_put_lkb(lkb); +	memset(&callbacks, 0, sizeof(callbacks)); -		cond_resched(); -		goto repeat; +	mutex_lock(&lkb->lkb_cb_mutex); +	if (!lkb->lkb_callbacks[0].seq) { +		/* no callback work exists, shouldn't happen */ +		log_error(ls, "dlm_callback_work %x no work", lkb->lkb_id); +		dlm_print_lkb(lkb); +		dlm_dump_lkb_callbacks(lkb);  	} -	spin_unlock(&ast_queue_lock); -} -static inline int no_asts(void) -{ -	int ret; +	for (i = 0; i < DLM_CALLBACKS_SIZE; i++) { +		rv = dlm_rem_lkb_callback(ls, lkb, &callbacks[i], &resid); +		if (rv < 0) +			break; +	} -	spin_lock(&ast_queue_lock); -	ret = list_empty(&ast_queue); -	spin_unlock(&ast_queue_lock); -	return ret; -} +	if (resid) { +		/* cbs remain, loop should have removed all, shouldn't happen */ +		log_error(ls, "dlm_callback_work %x resid %d", lkb->lkb_id, +			  resid); +		dlm_print_lkb(lkb); +		dlm_dump_lkb_callbacks(lkb); +	} +	mutex_unlock(&lkb->lkb_cb_mutex); -static int dlm_astd(void *data) -{ -	while (!kthread_should_stop()) { -		set_current_state(TASK_INTERRUPTIBLE); -		if (!test_bit(WAKE_ASTS, &astd_wakeflags)) -			schedule(); -		set_current_state(TASK_RUNNING); +	castfn = lkb->lkb_astfn; +	bastfn = lkb->lkb_bastfn; -		mutex_lock(&astd_running); -		if (test_and_clear_bit(WAKE_ASTS, &astd_wakeflags)) -			process_asts(); -		mutex_unlock(&astd_running); +	for (i = 0; i < DLM_CALLBACKS_SIZE; i++) { +		if (!callbacks[i].seq) +			break; +		if (callbacks[i].flags & DLM_CB_SKIP) { +			continue; +		} else if (callbacks[i].flags & DLM_CB_BAST) { +			bastfn(lkb->lkb_astparam, callbacks[i].mode); +		} else if (callbacks[i].flags & DLM_CB_CAST) { +			lkb->lkb_lksb->sb_status = callbacks[i].sb_status; +			lkb->lkb_lksb->sb_flags = callbacks[i].sb_flags; +			castfn(lkb->lkb_astparam); +		}  	} -	return 0; + +	/* undo kref_get from dlm_add_callback, may cause lkb to be freed */ +	dlm_put_lkb(lkb);  } -void dlm_astd_wake(void) +int dlm_callback_start(struct dlm_ls *ls)  { -	if (!no_asts()) { -		set_bit(WAKE_ASTS, &astd_wakeflags); -		wake_up_process(astd_task); +	ls->ls_callback_wq = alloc_workqueue("dlm_callback", +					     WQ_UNBOUND | WQ_MEM_RECLAIM, 0); +	if (!ls->ls_callback_wq) { +		log_print("can't start dlm_callback workqueue"); +		return -ENOMEM;  	} +	return 0;  } -int dlm_astd_start(void) +void dlm_callback_stop(struct dlm_ls *ls)  { -	struct task_struct *p; -	int error = 0; - -	INIT_LIST_HEAD(&ast_queue); -	spin_lock_init(&ast_queue_lock); -	mutex_init(&astd_running); - -	p = kthread_run(dlm_astd, NULL, "dlm_astd"); -	if (IS_ERR(p)) -		error = PTR_ERR(p); -	else -		astd_task = p; -	return error; +	if (ls->ls_callback_wq) +		destroy_workqueue(ls->ls_callback_wq);  } -void dlm_astd_stop(void) +void dlm_callback_suspend(struct dlm_ls *ls)  { -	kthread_stop(astd_task); -} +	set_bit(LSFL_CB_DELAY, &ls->ls_flags); -void dlm_astd_suspend(void) -{ -	mutex_lock(&astd_running); +	if (ls->ls_callback_wq) +		flush_workqueue(ls->ls_callback_wq);  } -void dlm_astd_resume(void) +void dlm_callback_resume(struct dlm_ls *ls)  { -	mutex_unlock(&astd_running); +	struct dlm_lkb *lkb, *safe; +	int count = 0; + +	clear_bit(LSFL_CB_DELAY, &ls->ls_flags); + +	if (!ls->ls_callback_wq) +		return; + +	mutex_lock(&ls->ls_cb_mutex); +	list_for_each_entry_safe(lkb, safe, &ls->ls_cb_delay, lkb_cb_list) { +		list_del_init(&lkb->lkb_cb_list); +		queue_work(ls->ls_callback_wq, &lkb->lkb_cb_work); +		count++; +	} +	mutex_unlock(&ls->ls_cb_mutex); + +	if (count) +		log_rinfo(ls, "dlm_callback_resume %d", count);  }  | 
