示例#1
0
void
dtrace_vtime_disable(void)
{
	dtrace_vtime_state_t state, nstate = 0;

	do {
		state = dtrace_vtime_active;

		switch (state) {
		case DTRACE_VTIME_ACTIVE:
			nstate = DTRACE_VTIME_INACTIVE;
			break;

		case DTRACE_VTIME_ACTIVE_TNF:
			nstate = DTRACE_VTIME_INACTIVE_TNF;
			break;

		case DTRACE_VTIME_INACTIVE:
		case DTRACE_VTIME_INACTIVE_TNF:
			panic("DTrace virtual time already disabled");
			/*NOTREACHED*/
		}

	} while	(dtrace_cas32((uint32_t *)&dtrace_vtime_active,
	    state, nstate) != state);
}
示例#2
0
void
dtrace_xcall2(processorid_t cpu, dtrace_xcall_t func, void *arg)
{	int	c;
	int	cpu_id = smp_processor_id();
	int	cpus_todo = 0;
# if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 24)
typedef struct cpumask cpumask_t;
//#define cpu_set(c, mask) cpumask_set_cpu(c, &(mask))
//#define cpus_clear(mask) cpumask_clear(&mask)
# endif
	cpumask_t mask;

	/***********************************************/
	/*   If  we had an internal panic, stop doing  */
	/*   xcalls.   Shouldnt  happen,  but  useful  */
	/*   during debugging so we can diagnose what  */
	/*   caused the panic.			       */
	/***********************************************/
	if (dtrace_shutdown)
		return;
	/***********************************************/
	/*   Special case - just 'us'.		       */
	/***********************************************/
	cnt_xcall1++;
	if (cpu_id == cpu) {
		local_irq_disable();
//dtrace_printf("[%d] sole cnt=%lu\n", smp_processor_id(), cnt_xcall1);
		func(arg);
		local_irq_enable();
		return;
	}

	/***********************************************/
	/*   Set  up  the  cpu  mask  to  do just the  */
	/*   relevant cpu.			       */
	/***********************************************/
	if (cpu != DTRACE_CPUALL) {
//dtrace_printf("just me %d %d\n", cpu_id, cpu);
		cpu = 1 << cpu;
	}


//dtrace_printf("xcall %d f=%p\n", cpu_id, func);
	cnt_xcall2++;
	if (xcall_levels[cpu_id]++)
		cnt_xcall3++;
	/***********************************************/
	/*   Set  up  the  rendezvous  with the other  */
	/*   targetted  cpus.  We use a nearly square  */
	/*   NCPU*NCPU matrix to allow for any cpu to  */
	/*   wait  for  any  other. We have two slots  */
	/*   per  cpu  -  because  we  may  be  in an  */
	/*   interrupt.				       */
	/*   					       */
	/*   The  interrupt  slave  will  service all  */
	/*   queued  calls  -  sometimes  it  will be  */
	/*   lucky and see multiple, especially if we  */
	/*   are heavily loaded.		       */
	/***********************************************/
	cpus_clear(mask);
	for (c = 0; c < nr_cpus; c++) {
		struct xcalls *xc = &xcalls[cpu_id][c];
		unsigned int cnt;

		/***********************************************/
		/*   Dont  set  ourselves  - we dont want our  */
		/*   cpu  to  be  taking an IPI interrupt and  */
		/*   doing   the   work   twice.   We  inline  */
		/*   ourselves below.			       */
		/***********************************************/
		if ((cpu & (1 << c)) == 0 || c == cpu_id) {
			continue;
		}

		/***********************************************/
		/*   Is  this  safe?  We want to avoid an IPI  */
		/*   call  if the other cpu is idle/not doing  */
		/*   dtrace  work.  If  thats the case and we  */
		/*   are  calling  dtrace_sync,  then  we can  */
		/*   avoid the xcall.			       */
		/***********************************************/
		if ((void *) func == (void *) dtrace_sync_func &&
		    cpu_core[c].cpuc_probe_level == 0) {
			cpu &= ~(1 << c);
			cnt_xcall7++;
			continue;
		}
//dtrace_printf("xcall %p\n", func);

		xc->xc_func = func;
		xc->xc_arg = arg;
		/***********************************************/
		/*   Spinlock  in  case  the  interrupt hasnt  */
		/*   fired.  This  should  be  very rare, and  */
		/*   when it happens, we would be hanging for  */
		/*   100m  iterations  (about  1s). We reduce  */
		/*   the   chance  of  a  hit  by  using  the  */
		/*   NCPU*NCPU*2 array approach. These things  */
		/*   happen  when buffers are full or user is  */
		/*   ^C-ing dtrace.			       */
		/***********************************************/
		for (cnt = 0; dtrace_cas32((void *) &xc->xc_state, XC_WORKING, XC_WORKING) == XC_WORKING; cnt++) {
			/***********************************************/
			/*   Avoid noise for tiny windows.	       */
			/***********************************************/
			if ((cnt == 0 && xcall_debug) || !(xcall_debug && cnt == 50)) {
				dtrace_printf("[%d] cpu%d in wrong state (state=%d)\n",
					smp_processor_id(), c, xc->xc_state);
			}
//			xcall_slave2();
			if (cnt == 100 * 1000 * 1000) {
				dtrace_printf("[%d] cpu%d - busting lock\n",
					smp_processor_id(), c);
				break;
			}
		}
		if ((cnt && xcall_debug) || (!xcall_debug && cnt > 50)) {
			dtrace_printf("[%d] cpu%d in wrong state (state=%d) %u cycles\n",
				smp_processor_id(), c, xc->xc_state, cnt);
		}
		/***********************************************/
		/*   As  soon  as  we set xc_state and BEFORE  */
		/*   the  apic  call,  the  cpu  may  see the  */
		/*   change  since  it  may  be taking an IPI  */
		/*   interrupt  for  someone else. We need to  */
		/*   be  careful  with  barriers  (I  think -  */
		/*   although    the   clflush/wmb   may   be  */
		/*   redundant).			       */
		/***********************************************/
		xc->xc_state = XC_WORKING;
//		clflush(&xc->xc_state);
//		smp_wmb();
		cpu_set(c, mask);
		cpus_todo++;
	}

	smp_mb();

	/***********************************************/
	/*   Now tell the other cpus to do some work.  */
	/***********************************************/
	if (cpus_todo)
		send_ipi_interrupt(&mask, ipi_vector);

	/***********************************************/
	/*   Check for ourselves.		       */
	/***********************************************/
	if (cpu & (1 << cpu_id)) {
		func(arg);
	}

	if (xcall_debug)
		dtrace_printf("[%d] getting ready.... (%ld) mask=%x func=%p\n", smp_processor_id(), cnt_xcall1, *(int *) &mask, func);

	/***********************************************/
	/*   Wait for the cpus we invoked the IPI on.  */
	/*   Cycle  thru  the  cpus,  to avoid mutual  */
	/*   deadlock  between one cpu trying to call  */
	/*   us whilst we are calling them.	       */
	/***********************************************/
	while (cpus_todo > 0) {
		for (c = 0; c < nr_cpus && cpus_todo > 0; c++) {
			xcall_slave2();
			if (c == cpu_id || (cpu & (1 << c)) == 0)
				continue;

			/***********************************************/
			/*   Wait  a  little  while  for  this cpu to  */
			/*   respond before going on to the next one.  */
			/***********************************************/
			if (ack_wait(c, 100)) {
				cpus_todo--;
				cpu &= ~(1 << c);
			}
		}
	}
//	smp_mb();

	xcall_levels[cpu_id]--;
}
示例#3
0
int
ack_wait(int c, int attempts)
{
	unsigned long cnt = 0;
	int	cnt1 = 0;
	volatile struct xcalls *xc = &xcalls[smp_processor_id()][c];

	/***********************************************/
	/*   Avoid holding on to a stale cache line.   */
	/***********************************************/
	while (dtrace_cas32((void *) &xc->xc_state, XC_WORKING, XC_WORKING) != XC_IDLE) {
		if (attempts-- <= 0)
			return 0;

		barrier();

		/***********************************************/
		/*   Be HT friendly.			       */
		/***********************************************/
//		smt_pause();

		cnt_xcall6++;
		/***********************************************/
		/*   Keep track of the max.		       */
		/***********************************************/
		if (cnt > cnt_xcall5)
			cnt_xcall5 = cnt;

		/***********************************************/
		/*   On  my  Dual Core 2.26GHz system, we are  */
		/*   seeing counters in the range of hundreds  */
		/*   to  maybe  2,000,000  for  more  extreme  */
		/*   cases.  (This  is  inside  a VM). During  */
		/*   debugging,  we  found  problems with the  */
		/*   two  cores  not  seeing  each  other  --  */
		/*   possibly because I wasnt doing the right  */
		/*   things to ensure memory barriers were in  */
		/*   place.				       */
		/*   					       */
		/*   We  dont  want  to  wait forever because  */
		/*   that  will  crash/hang your machine, but  */
		/*   we  do  need to give up if its taken far  */
		/*   too long.				       */
		/***********************************************/
//		if (cnt++ == 50 * 1000 * 1000UL) {
		if (cnt++ == 1 * 1000 * 1000UL) {
			cnt = 0;
			cnt_xcall4++;

			if (cnt1 == 0) {
				/***********************************************/
				/*   Looks like we are having trouble getting  */
				/*   the interrupt, so try for an NMI.	       */
				/***********************************************/
				cpumask_t mask;
				cpus_clear(mask);
				cpu_set(c, mask);
//				nmi_masks[c] = 1;
//				send_ipi_interrupt(&mask, 2); //NMI_VECTOR);
			}

			if (1) {
//				set_console_on(1);
				dtrace_printf("ack_wait cpu=%d xcall %staking too long! c=%d [xcall1=%lu]\n", 
					smp_processor_id(), 
					cnt1 ? "STILL " : "",
					c, cnt_xcall1);
				//dump_stack();
//				set_console_on(0);
			}

			if (cnt1++ > 3) {
				dump_xcalls();
				dtrace_linux_panic("xcall taking too long");
				break;
			}
		}
	}

	if (xcall_debug) {
		dtrace_printf("[%d] ack_wait finished c=%d cnt=%lu (max=%lu)\n", smp_processor_id(), c, cnt, cnt_xcall5);
	}
	return 1;
}