⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lock.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
	/*	 * 6-5: But the default algorithm for deciding whether to grant or	 * queue conversion requests does not by itself guarantee that such	 * requests are serviced on a "first come first serve" basis.  This, in	 * turn, can lead to a phenomenon known as "indefinate postponement".	 *	 * 6-7: This issue is dealt with by using the optional QUECVT flag with	 * the system service employed to request a lock conversion.  This flag	 * forces certain conversion requests to be queued, even if they are	 * compatible with the granted modes of other locks on the same	 * resource.  Thus, the use of this flag results in conversion requests	 * being ordered on a "first come first servce" basis.	 *	 * DCT: This condition is all about new conversions being able to occur	 * "in place" while the lock remains on the granted queue (assuming	 * nothing else conflicts.)  IOW if QUECVT isn't set, a conversion	 * doesn't _have_ to go onto the convert queue where it's processed in	 * order.  The "now" variable is necessary to distinguish converts	 * being received and processed for the first time now, because once a	 * convert is moved to the conversion queue the condition below applies	 * requiring fifo granting.	 */	if (now && conv && !(lkb->lkb_exflags & DLM_LKF_QUECVT))		return 1;	/*	 * The NOORDER flag is set to avoid the standard vms rules on grant	 * order.	 */	if (lkb->lkb_exflags & DLM_LKF_NOORDER)		return 1;	/*	 * 6-3: Once in that queue [CONVERTING], a conversion request cannot be	 * granted until all other conversion requests ahead of it are granted	 * and/or canceled.	 */	if (!now && conv && first_in_list(lkb, &r->res_convertqueue))		return 1;	/*	 * 6-4: By default, a new request is immediately granted only if all	 * three of the following conditions are satisfied when the request is	 * issued:	 * - The queue of ungranted conversion requests for the resource is	 *   empty.	 * - The queue of ungranted new requests for the resource is empty.	 * - The mode of the new request is compatible with the most	 *   restrictive mode of all granted locks on the resource.	 */	if (now && !conv && list_empty(&r->res_convertqueue) &&	    list_empty(&r->res_waitqueue))		return 1;	/*	 * 6-4: Once a lock request is in the queue of ungranted new requests,	 * it cannot be granted until the queue of ungranted conversion	 * requests is empty, all ungranted new requests ahead of it are	 * granted and/or canceled, and it is compatible with the granted mode	 * of the most restrictive lock granted on the resource.	 */	if (!now && !conv && list_empty(&r->res_convertqueue) &&	    first_in_list(lkb, &r->res_waitqueue))		return 1; out:	return 0;}static int can_be_granted(struct dlm_rsb *r, struct dlm_lkb *lkb, int now,			  int *err){	int rv;	int8_t alt = 0, rqmode = lkb->lkb_rqmode;	int8_t is_convert = (lkb->lkb_grmode != DLM_LOCK_IV);	if (err)		*err = 0;	rv = _can_be_granted(r, lkb, now);	if (rv)		goto out;	/*	 * The CONVDEADLK flag is non-standard and tells the dlm to resolve	 * conversion deadlocks by demoting grmode to NL, otherwise the dlm	 * cancels one of the locks.	 */	if (is_convert && can_be_queued(lkb) &&	    conversion_deadlock_detect(r, lkb)) {		if (lkb->lkb_exflags & DLM_LKF_CONVDEADLK) {			lkb->lkb_grmode = DLM_LOCK_NL;			lkb->lkb_sbflags |= DLM_SBF_DEMOTED;		} else if (!(lkb->lkb_exflags & DLM_LKF_NODLCKWT)) {			if (err)				*err = -EDEADLK;			else {				log_print("can_be_granted deadlock %x now %d",					  lkb->lkb_id, now);				dlm_dump_rsb(r);			}		}		goto out;	}	/*	 * The ALTPR and ALTCW flags are non-standard and tell the dlm to try	 * to grant a request in a mode other than the normal rqmode.  It's a	 * simple way to provide a big optimization to applications that can	 * use them.	 */	if (rqmode != DLM_LOCK_PR && (lkb->lkb_exflags & DLM_LKF_ALTPR))		alt = DLM_LOCK_PR;	else if (rqmode != DLM_LOCK_CW && (lkb->lkb_exflags & DLM_LKF_ALTCW))		alt = DLM_LOCK_CW;	if (alt) {		lkb->lkb_rqmode = alt;		rv = _can_be_granted(r, lkb, now);		if (rv)			lkb->lkb_sbflags |= DLM_SBF_ALTMODE;		else			lkb->lkb_rqmode = rqmode;	} out:	return rv;}/* FIXME: I don't think that can_be_granted() can/will demote or find deadlock   for locks pending on the convert list.  Once verified (watch for these   log_prints), we should be able to just call _can_be_granted() and not   bother with the demote/deadlk cases here (and there's no easy way to deal   with a deadlk here, we'd have to generate something like grant_lock with   the deadlk error.) *//* Returns the highest requested mode of all blocked conversions; sets   cw if there's a blocked conversion to DLM_LOCK_CW. */static int grant_pending_convert(struct dlm_rsb *r, int high, int *cw){	struct dlm_lkb *lkb, *s;	int hi, demoted, quit, grant_restart, demote_restart;	int deadlk;	quit = 0; restart:	grant_restart = 0;	demote_restart = 0;	hi = DLM_LOCK_IV;	list_for_each_entry_safe(lkb, s, &r->res_convertqueue, lkb_statequeue) {		demoted = is_demoted(lkb);		deadlk = 0;		if (can_be_granted(r, lkb, 0, &deadlk)) {			grant_lock_pending(r, lkb);			grant_restart = 1;			continue;		}		if (!demoted && is_demoted(lkb)) {			log_print("WARN: pending demoted %x node %d %s",				  lkb->lkb_id, lkb->lkb_nodeid, r->res_name);			demote_restart = 1;			continue;		}		if (deadlk) {			log_print("WARN: pending deadlock %x node %d %s",				  lkb->lkb_id, lkb->lkb_nodeid, r->res_name);			dlm_dump_rsb(r);			continue;		}		hi = max_t(int, lkb->lkb_rqmode, hi);		if (cw && lkb->lkb_rqmode == DLM_LOCK_CW)			*cw = 1;	}	if (grant_restart)		goto restart;	if (demote_restart && !quit) {		quit = 1;		goto restart;	}	return max_t(int, high, hi);}static int grant_pending_wait(struct dlm_rsb *r, int high, int *cw){	struct dlm_lkb *lkb, *s;	list_for_each_entry_safe(lkb, s, &r->res_waitqueue, lkb_statequeue) {		if (can_be_granted(r, lkb, 0, NULL))			grant_lock_pending(r, lkb);                else {			high = max_t(int, lkb->lkb_rqmode, high);			if (lkb->lkb_rqmode == DLM_LOCK_CW)				*cw = 1;		}	}	return high;}/* cw of 1 means there's a lock with a rqmode of DLM_LOCK_CW that's blocked   on either the convert or waiting queue.   high is the largest rqmode of all locks blocked on the convert or   waiting queue. */static int lock_requires_bast(struct dlm_lkb *gr, int high, int cw){	if (gr->lkb_grmode == DLM_LOCK_PR && cw) {		if (gr->lkb_highbast < DLM_LOCK_EX)			return 1;		return 0;	}	if (gr->lkb_highbast < high &&	    !__dlm_compat_matrix[gr->lkb_grmode+1][high+1])		return 1;	return 0;}static void grant_pending_locks(struct dlm_rsb *r){	struct dlm_lkb *lkb, *s;	int high = DLM_LOCK_IV;	int cw = 0;	DLM_ASSERT(is_master(r), dlm_dump_rsb(r););	high = grant_pending_convert(r, high, &cw);	high = grant_pending_wait(r, high, &cw);	if (high == DLM_LOCK_IV)		return;	/*	 * If there are locks left on the wait/convert queue then send blocking	 * ASTs to granted locks based on the largest requested mode (high)	 * found above.	 */	list_for_each_entry_safe(lkb, s, &r->res_grantqueue, lkb_statequeue) {		if (lkb->lkb_bastaddr && lock_requires_bast(lkb, high, cw)) {			if (cw && high == DLM_LOCK_PR)				queue_bast(r, lkb, DLM_LOCK_CW);			else				queue_bast(r, lkb, high);			lkb->lkb_highbast = high;		}	}}static int modes_require_bast(struct dlm_lkb *gr, struct dlm_lkb *rq){	if ((gr->lkb_grmode == DLM_LOCK_PR && rq->lkb_rqmode == DLM_LOCK_CW) ||	    (gr->lkb_grmode == DLM_LOCK_CW && rq->lkb_rqmode == DLM_LOCK_PR)) {		if (gr->lkb_highbast < DLM_LOCK_EX)			return 1;		return 0;	}	if (gr->lkb_highbast < rq->lkb_rqmode && !modes_compat(gr, rq))		return 1;	return 0;}static void send_bast_queue(struct dlm_rsb *r, struct list_head *head,			    struct dlm_lkb *lkb){	struct dlm_lkb *gr;	list_for_each_entry(gr, head, lkb_statequeue) {		if (gr->lkb_bastaddr && modes_require_bast(gr, lkb)) {			queue_bast(r, gr, lkb->lkb_rqmode);			gr->lkb_highbast = lkb->lkb_rqmode;		}	}}static void send_blocking_asts(struct dlm_rsb *r, struct dlm_lkb *lkb){	send_bast_queue(r, &r->res_grantqueue, lkb);}static void send_blocking_asts_all(struct dlm_rsb *r, struct dlm_lkb *lkb){	send_bast_queue(r, &r->res_grantqueue, lkb);	send_bast_queue(r, &r->res_convertqueue, lkb);}/* set_master(r, lkb) -- set the master nodeid of a resource   The purpose of this function is to set the nodeid field in the given   lkb using the nodeid field in the given rsb.  If the rsb's nodeid is   known, it can just be copied to the lkb and the function will return   0.  If the rsb's nodeid is _not_ known, it needs to be looked up   before it can be copied to the lkb.   When the rsb nodeid is being looked up remotely, the initial lkb   causing the lookup is kept on the ls_waiters list waiting for the   lookup reply.  Other lkb's waiting for the same rsb lookup are kept   on the rsb's res_lookup list until the master is verified.   Return values:   0: nodeid is set in rsb/lkb and the caller should go ahead and use it   1: the rsb master is not available and the lkb has been placed on      a wait queue*/static int set_master(struct dlm_rsb *r, struct dlm_lkb *lkb){	struct dlm_ls *ls = r->res_ls;	int error, dir_nodeid, ret_nodeid, our_nodeid = dlm_our_nodeid();	if (rsb_flag(r, RSB_MASTER_UNCERTAIN)) {		rsb_clear_flag(r, RSB_MASTER_UNCERTAIN);		r->res_first_lkid = lkb->lkb_id;		lkb->lkb_nodeid = r->res_nodeid;		return 0;	}	if (r->res_first_lkid && r->res_first_lkid != lkb->lkb_id) {		list_add_tail(&lkb->lkb_rsb_lookup, &r->res_lookup);		return 1;	}	if (r->res_nodeid == 0) {		lkb->lkb_nodeid = 0;		return 0;	}	if (r->res_nodeid > 0) {		lkb->lkb_nodeid = r->res_nodeid;		return 0;	}	DLM_ASSERT(r->res_nodeid == -1, dlm_dump_rsb(r););	dir_nodeid = dlm_dir_nodeid(r);	if (dir_nodeid != our_nodeid) {		r->res_first_lkid = lkb->lkb_id;		send_lookup(r, lkb);		return 1;	}	for (;;) {		/* It's possible for dlm_scand to remove an old rsb for		   this same resource from the toss list, us to create		   a new one, look up the master locally, and find it		   already exists just before dlm_scand does the		   dir_remove() on the previous rsb. */		error = dlm_dir_lookup(ls, our_nodeid, r->res_name,				       r->res_length, &ret_nodeid);		if (!error)			break;		log_debug(ls, "dir_lookup error %d %s", error, r->res_name);		schedule();	}	if (ret_nodeid == our_nodeid) {		r->res_first_lkid = 0;		r->res_nodeid = 0;		lkb->lkb_nodeid = 0;	} else {		r->res_first_lkid = lkb->lkb_id;		r->res_nodeid = ret_nodeid;		lkb->lkb_nodeid = ret_nodeid;	}	return 0;}static void process_lookup_list(struct dlm_rsb *r){	struct dlm_lkb *lkb, *safe;	list_for_each_entry_safe(lkb, safe, &r->res_lookup, lkb_rsb_lookup) {		list_del_init(&lkb->lkb_rsb_lookup);		_request_lock(r, lkb);		schedule();	}}/* confirm_master -- confirm (or deny) an rsb's master nodeid */static void confirm_master(struct dlm_rsb *r, int error){	struct dlm_lkb *lkb;	if (!r->res_first_lkid)		return;	switch (error) {	case 0:	case -EINPROGRESS:		r->res_first_lkid = 0;		process_lookup_list(r);		break;	case -EAGAIN:		/* the remote master didn't queue our NOQUEUE request;		   make a waiting lkb the first_lkid */		r->res_first_lkid = 0;		if (!list_empty(&r->res_lookup)) {			lkb = list_entry(r->res_lookup.next, struct dlm_lkb,					 lkb_rsb_lookup);			list_del_init(&lkb->lkb_rsb_lookup);			r->res_first_lkid = lkb->lkb_id;			_request_lock(r, lkb);		} else			r->res_nodeid = -1;		break;	default:		log_error(r->res_ls, "confirm_master unknown error %d", error);	}}static int set_lock_args(int mode, struct dlm_lksb *lksb, uint32_t flags,			 int namelen, unsigned long timeout_cs, void *ast,			 void *astarg, void *bast, struct dlm_args *args){	int rv = -EINVAL;	/* check for invalid arg usage */	if (mode < 0 || mode > DLM_LOCK_EX)		goto out;	if (!(flags & DLM_LKF_CONVERT) && (namelen > DLM_RESNAME_MAXLEN))		goto out;	if (flags & DLM_LKF_CANCEL)		goto out;	if (flags & DLM_LKF_QUECVT && !(flags & DLM_LKF_CONVERT))		goto out;	if (flags & DLM_LKF_CONVDEADLK && !(flags & DLM_LKF_CONVERT))		goto out;	if (flags & DLM_LKF_CONVDEADLK && flags & DLM_LKF_NOQUEUE)		goto out;	if (flags & DLM_LKF_EXPEDITE && flags & DLM_LKF_CONVERT)		goto out;	if (flags & DLM_LKF_EXPEDITE && flags & DLM_LKF_QUECVT)		goto out;	if (flags & DLM_LKF_EXPEDITE && flags & DLM_LKF_NOQUEUE)		goto out;	if (flags & DLM_LKF_EXPEDITE && mode != DLM_LOCK_NL)		goto out;	if (!ast || !lksb)		goto out;	if (flags & DLM_LKF_VALBLK && !lksb->sb_lvbptr)		goto out;	if (flags & DLM_LKF_CONVERT && !lksb->sb_lkid)		goto out;	/* these args will be copied to the lkb in validate_lock_args,	   it cannot be done now because when converting locks, fields in	   an active lkb cannot be modified before locking the rsb */	args->flags = flags;	args->astaddr = ast;	args->astparam = (long) astarg;	args->bastaddr = bast;	args->timeout = timeout_cs;	args->mode = mode;	args->lksb = lksb;	rv = 0; out:	return rv;}static int set_unlock_args(uint32_t flags, void *astarg, struct dlm_args *args){	if (flags & ~(DLM_LKF_CANCEL | DLM_LKF_VALBLK | DLM_LKF_IVVALBLK | 		      DLM_LKF_FORCEUNLOCK))		return -EINVAL;	if (flags & DLM_LKF_CANCEL && flags & DLM_LKF_FORCEUNLOCK)		return -EINVAL;	args->flags = flags;	args->astparam = (long) astarg;	return 0;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -