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

📄 rcupreempt.c

📁 Kernel code of linux kernel
💻 C
📖 第 1 页 / 共 3 页
字号:
		/*		 * Since we can't determine the dynamic tick mode from		 * the rcu_dyntick_sched.dynticks after this routine,		 * we use a second flag to acknowledge that we came		 * from an idle state with ticks stopped.		 */		per_cpu(rcu_update_flag, cpu)++;		/*		 * If we take an NMI/SMI now, they will also increment		 * the rcu_update_flag, and will not update the		 * rcu_dyntick_sched.dynticks on exit. That is for		 * this IRQ to do.		 */	}}/** * rcu_irq_exit - Called from exiting Hard irq context. * * If the CPU was idle with dynamic ticks active, update the * rcu_dyntick_sched.dynticks to put let the RCU handling be * aware that the CPU is going back to idle with no ticks. */void rcu_irq_exit(void){	int cpu = smp_processor_id();	struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);	/*	 * rcu_update_flag is set if we interrupted the CPU	 * when it was idle with ticks stopped.	 * Once this occurs, we keep track of interrupt nesting	 * because a NMI/SMI could also come in, and we still	 * only want the IRQ that started the increment of the	 * rcu_dyntick_sched.dynticks to be the one that modifies	 * it on exit.	 */	if (per_cpu(rcu_update_flag, cpu)) {		if (--per_cpu(rcu_update_flag, cpu))			return;		/* This must match the interrupt nesting */		WARN_ON(in_interrupt());		/*		 * If an NMI/SMI happens now we are still		 * protected by the rcu_dyntick_sched.dynticks being odd.		 */		/*		 * The following memory barrier ensures that any		 * rcu_read_unlock() primitives in the irq handler		 * are seen by other CPUs to preceed the following		 * increment to rcu_dyntick_sched.dynticks. This		 * is required in order for other CPUs to determine		 * when it is safe to advance the RCU grace-period		 * state machine.		 */		smp_mb(); /* see above block comment. */		rdssp->dynticks++;		WARN_ON(rdssp->dynticks & 0x1);	}}static void dyntick_save_progress_counter(int cpu){	struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);	rdssp->dynticks_snap = rdssp->dynticks;}static inline intrcu_try_flip_waitack_needed(int cpu){	long curr;	long snap;	struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);	curr = rdssp->dynticks;	snap = rdssp->dynticks_snap;	smp_mb(); /* force ordering with cpu entering/leaving dynticks. */	/*	 * If the CPU remained in dynticks mode for the entire time	 * and didn't take any interrupts, NMIs, SMIs, or whatever,	 * then it cannot be in the middle of an rcu_read_lock(), so	 * the next rcu_read_lock() it executes must use the new value	 * of the counter.  So we can safely pretend that this CPU	 * already acknowledged the counter.	 */	if ((curr == snap) && ((curr & 0x1) == 0))		return 0;	/*	 * If the CPU passed through or entered a dynticks idle phase with	 * no active irq handlers, then, as above, we can safely pretend	 * that this CPU already acknowledged the counter.	 */	if ((curr - snap) > 2 || (curr & 0x1) == 0)		return 0;	/* We need this CPU to explicitly acknowledge the counter flip. */	return 1;}static inline intrcu_try_flip_waitmb_needed(int cpu){	long curr;	long snap;	struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);	curr = rdssp->dynticks;	snap = rdssp->dynticks_snap;	smp_mb(); /* force ordering with cpu entering/leaving dynticks. */	/*	 * If the CPU remained in dynticks mode for the entire time	 * and didn't take any interrupts, NMIs, SMIs, or whatever,	 * then it cannot have executed an RCU read-side critical section	 * during that time, so there is no need for it to execute a	 * memory barrier.	 */	if ((curr == snap) && ((curr & 0x1) == 0))		return 0;	/*	 * If the CPU either entered or exited an outermost interrupt,	 * SMI, NMI, or whatever handler, then we know that it executed	 * a memory barrier when doing so.  So we don't need another one.	 */	if (curr != snap)		return 0;	/* We need the CPU to execute a memory barrier. */	return 1;}static void dyntick_save_progress_counter_sched(int cpu){	struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);	rdssp->sched_dynticks_snap = rdssp->dynticks;}static int rcu_qsctr_inc_needed_dyntick(int cpu){	long curr;	long snap;	struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);	curr = rdssp->dynticks;	snap = rdssp->sched_dynticks_snap;	smp_mb(); /* force ordering with cpu entering/leaving dynticks. */	/*	 * If the CPU remained in dynticks mode for the entire time	 * and didn't take any interrupts, NMIs, SMIs, or whatever,	 * then it cannot be in the middle of an rcu_read_lock(), so	 * the next rcu_read_lock() it executes must use the new value	 * of the counter.  Therefore, this CPU has been in a quiescent	 * state the entire time, and we don't need to wait for it.	 */	if ((curr == snap) && ((curr & 0x1) == 0))		return 0;	/*	 * If the CPU passed through or entered a dynticks idle phase with	 * no active irq handlers, then, as above, this CPU has already	 * passed through a quiescent state.	 */	if ((curr - snap) > 2 || (snap & 0x1) == 0)		return 0;	/* We need this CPU to go through a quiescent state. */	return 1;}#else /* !CONFIG_NO_HZ */# define dyntick_save_progress_counter(cpu)		do { } while (0)# define rcu_try_flip_waitack_needed(cpu)		(1)# define rcu_try_flip_waitmb_needed(cpu)		(1)# define dyntick_save_progress_counter_sched(cpu)	do { } while (0)# define rcu_qsctr_inc_needed_dyntick(cpu)		(1)#endif /* CONFIG_NO_HZ */static void save_qsctr_sched(int cpu){	struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);	rdssp->sched_qs_snap = rdssp->sched_qs;}static inline int rcu_qsctr_inc_needed(int cpu){	struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);	/*	 * If there has been a quiescent state, no more need to wait	 * on this CPU.	 */	if (rdssp->sched_qs != rdssp->sched_qs_snap) {		smp_mb(); /* force ordering with cpu entering schedule(). */		return 0;	}	/* We need this CPU to go through a quiescent state. */	return 1;}/* * Get here when RCU is idle.  Decide whether we need to * move out of idle state, and return non-zero if so. * "Straightforward" approach for the moment, might later * use callback-list lengths, grace-period duration, or * some such to determine when to exit idle state. * Might also need a pre-idle test that does not acquire * the lock, but let's get the simple case working first... */static intrcu_try_flip_idle(void){	int cpu;	RCU_TRACE_ME(rcupreempt_trace_try_flip_i1);	if (!rcu_pending(smp_processor_id())) {		RCU_TRACE_ME(rcupreempt_trace_try_flip_ie1);		return 0;	}	/*	 * Do the flip.	 */	RCU_TRACE_ME(rcupreempt_trace_try_flip_g1);	rcu_ctrlblk.completed++;  /* stands in for rcu_try_flip_g2 */	/*	 * Need a memory barrier so that other CPUs see the new	 * counter value before they see the subsequent change of all	 * the rcu_flip_flag instances to rcu_flipped.	 */	smp_mb();	/* see above block comment. */	/* Now ask each CPU for acknowledgement of the flip. */	for_each_cpu_mask_nr(cpu, rcu_cpu_online_map) {		per_cpu(rcu_flip_flag, cpu) = rcu_flipped;		dyntick_save_progress_counter(cpu);	}	return 1;}/* * Wait for CPUs to acknowledge the flip. */static intrcu_try_flip_waitack(void){	int cpu;	RCU_TRACE_ME(rcupreempt_trace_try_flip_a1);	for_each_cpu_mask_nr(cpu, rcu_cpu_online_map)		if (rcu_try_flip_waitack_needed(cpu) &&		    per_cpu(rcu_flip_flag, cpu) != rcu_flip_seen) {			RCU_TRACE_ME(rcupreempt_trace_try_flip_ae1);			return 0;		}	/*	 * Make sure our checks above don't bleed into subsequent	 * waiting for the sum of the counters to reach zero.	 */	smp_mb();	/* see above block comment. */	RCU_TRACE_ME(rcupreempt_trace_try_flip_a2);	return 1;}/* * Wait for collective ``last'' counter to reach zero, * then tell all CPUs to do an end-of-grace-period memory barrier. */static intrcu_try_flip_waitzero(void){	int cpu;	int lastidx = !(rcu_ctrlblk.completed & 0x1);	int sum = 0;	/* Check to see if the sum of the "last" counters is zero. */	RCU_TRACE_ME(rcupreempt_trace_try_flip_z1);	for_each_cpu_mask_nr(cpu, rcu_cpu_online_map)		sum += RCU_DATA_CPU(cpu)->rcu_flipctr[lastidx];	if (sum != 0) {		RCU_TRACE_ME(rcupreempt_trace_try_flip_ze1);		return 0;	}	/*	 * This ensures that the other CPUs see the call for	 * memory barriers -after- the sum to zero has been	 * detected here	 */	smp_mb();  /*  ^^^^^^^^^^^^ */	/* Call for a memory barrier from each CPU. */	for_each_cpu_mask_nr(cpu, rcu_cpu_online_map) {		per_cpu(rcu_mb_flag, cpu) = rcu_mb_needed;		dyntick_save_progress_counter(cpu);	}	RCU_TRACE_ME(rcupreempt_trace_try_flip_z2);	return 1;}/* * Wait for all CPUs to do their end-of-grace-period memory barrier. * Return 0 once all CPUs have done so. */static intrcu_try_flip_waitmb(void){	int cpu;	RCU_TRACE_ME(rcupreempt_trace_try_flip_m1);	for_each_cpu_mask_nr(cpu, rcu_cpu_online_map)		if (rcu_try_flip_waitmb_needed(cpu) &&		    per_cpu(rcu_mb_flag, cpu) != rcu_mb_done) {			RCU_TRACE_ME(rcupreempt_trace_try_flip_me1);			return 0;		}	smp_mb(); /* Ensure that the above checks precede any following flip. */	RCU_TRACE_ME(rcupreempt_trace_try_flip_m2);	return 1;}/* * Attempt a single flip of the counters.  Remember, a single flip does * -not- constitute a grace period.  Instead, the interval between * at least GP_STAGES consecutive flips is a grace period. * * If anyone is nuts enough to run this CONFIG_PREEMPT_RCU implementation * on a large SMP, they might want to use a hierarchical organization of * the per-CPU-counter pairs. */static void rcu_try_flip(void){	unsigned long flags;	RCU_TRACE_ME(rcupreempt_trace_try_flip_1);	if (unlikely(!spin_trylock_irqsave(&rcu_ctrlblk.fliplock, flags))) {		RCU_TRACE_ME(rcupreempt_trace_try_flip_e1);		return;	}	/*	 * Take the next transition(s) through the RCU grace-period	 * flip-counter state machine.	 */	switch (rcu_ctrlblk.rcu_try_flip_state) {	case rcu_try_flip_idle_state:		if (rcu_try_flip_idle())			rcu_ctrlblk.rcu_try_flip_state =				rcu_try_flip_waitack_state;		break;	case rcu_try_flip_waitack_state:		if (rcu_try_flip_waitack())			rcu_ctrlblk.rcu_try_flip_state =				rcu_try_flip_waitzero_state;		break;	case rcu_try_flip_waitzero_state:		if (rcu_try_flip_waitzero())			rcu_ctrlblk.rcu_try_flip_state =				rcu_try_flip_waitmb_state;		break;	case rcu_try_flip_waitmb_state:		if (rcu_try_flip_waitmb())			rcu_ctrlblk.rcu_try_flip_state =				rcu_try_flip_idle_state;	}	spin_unlock_irqrestore(&rcu_ctrlblk.fliplock, flags);}/* * Check to see if this CPU needs to do a memory barrier in order to * ensure that any prior RCU read-side critical sections have committed * their counter manipulations and critical-section memory references * before declaring the grace period to be completed. */static void rcu_check_mb(int cpu){	if (per_cpu(rcu_mb_flag, cpu) == rcu_mb_needed) {		smp_mb();  /* Ensure RCU read-side accesses are visible. */		per_cpu(rcu_mb_flag, cpu) = rcu_mb_done;	}}void rcu_check_callbacks(int cpu, int user){	unsigned long flags;	struct rcu_data *rdp = RCU_DATA_CPU(cpu);	/*	 * If this CPU took its interrupt from user mode or from the	 * idle loop, and this is not a nested interrupt, then	 * this CPU has to have exited all prior preept-disable	 * sections of code.  So increment the counter to note this.	 *	 * The memory barrier is needed to handle the case where	 * writes from a preempt-disable section of code get reordered	 * into schedule() by this CPU's write buffer.  So the memory	 * barrier makes sure that the rcu_qsctr_inc() is seen by other	 * CPUs to happen after any such write.	 */	if (user ||	    (idle_cpu(cpu) && !in_softirq() &&	     hardirq_count() <= (1 << HARDIRQ_SHIFT))) {		smp_mb();	/* Guard against aggressive schedule(). */	     	rcu_qsctr_inc(cpu);	}	rcu_check_mb(cpu);	if (rcu_ctrlblk.completed == rdp->completed)		rcu_try_flip();	spin_lock_irqsave(&rdp->lock, flags);	RCU_TRACE_RDP(rcupreempt_trace_check_callbacks, rdp);	__rcu_advance_callbacks(rdp);	if (rdp->donelist == NULL) {		spin_unlock_irqrestore(&rdp->lock, flags);	} else {		spin_unlock_irqrestore(&rdp->lock, flags);		raise_softirq(RCU_SOFTIRQ);	}}/* * Needed by dynticks, to make sure all RCU processing has finished * when we go idle: */void rcu_advance_callbacks(int cpu, int user){	unsigned long flags;	struct rcu_data *rdp = RCU_DATA_CPU(cpu);	if (rcu_ctrlblk.completed == rdp->completed) {		rcu_try_flip();		if (rcu_ctrlblk.completed == rdp->completed)			return;	}	spin_lock_irqsave(&rdp->lock, flags);	RCU_TRACE_RDP(rcupreempt_trace_check_callbacks, rdp);	__rcu_advance_callbacks(rdp);	spin_unlock_irqrestore(&rdp->lock, flags);}#ifdef CONFIG_HOTPLUG_CPU#define rcu_offline_cpu_enqueue(srclist, srctail, dstlist, dsttail) do { \		*dsttail = srclist; \		if (srclist != NULL) { \			dsttail = srctail; \			srclist = NULL; \			srctail = &srclist;\		} \	} while (0)void rcu_offline_cpu(int cpu){	int i;	struct rcu_head *list = NULL;	unsigned long flags;	struct rcu_data *rdp = RCU_DATA_CPU(cpu);	struct rcu_head *schedlist = NULL;	struct rcu_head **schedtail = &schedlist;

⌨️ 快捷键说明

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