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

📄 time.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
	atomic_inc(sw_ptr);}/* * Make get_sync_clock return 0 again. * Needs to be called from a context disabled for preemption. */static void etr_enable_sync_clock(void){	atomic_t *sw_ptr = &__get_cpu_var(etr_sync_word);	atomic_set_mask(0x80000000, sw_ptr);}/* * Reset ETR attachment. */static void etr_reset(void){	etr_eacr =  (struct etr_eacr) {		.e0 = 0, .e1 = 0, ._pad0 = 4, .dp = 0,		.p0 = 0, .p1 = 0, ._pad1 = 0, .ea = 0,		.es = 0, .sl = 0 };	if (etr_setr(&etr_eacr) == 0)		etr_tolec = get_clock();	else {		set_bit(ETR_FLAG_ENOSYS, &etr_flags);		if (etr_port0_online || etr_port1_online) {			printk(KERN_WARNING "Running on non ETR capable "			       "machine, only local mode available.\n");			etr_port0_online = etr_port1_online = 0;		}	}}static int __init etr_init(void){	struct etr_aib aib;	if (test_bit(ETR_FLAG_ENOSYS, &etr_flags))		return 0;	/* Check if this machine has the steai instruction. */	if (etr_steai(&aib, ETR_STEAI_STEPPING_PORT) == 0)		set_bit(ETR_FLAG_STEAI, &etr_flags);	setup_timer(&etr_timer, etr_timeout, 0UL);	if (!etr_port0_online && !etr_port1_online)		set_bit(ETR_FLAG_EACCES, &etr_flags);	if (etr_port0_online) {		set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events);		schedule_work(&etr_work);	}	if (etr_port1_online) {		set_bit(ETR_EVENT_PORT1_CHANGE, &etr_events);		schedule_work(&etr_work);	}	return 0;}arch_initcall(etr_init);/* * Two sorts of ETR machine checks. The architecture reads: * "When a machine-check niterruption occurs and if a switch-to-local or *  ETR-sync-check interrupt request is pending but disabled, this pending *  disabled interruption request is indicated and is cleared". * Which means that we can get etr_switch_to_local events from the machine * check handler although the interruption condition is disabled. Lovely.. *//* * Switch to local machine check. This is called when the last usable * ETR port goes inactive. After switch to local the clock is not in sync. */void etr_switch_to_local(void){	if (!etr_eacr.sl)		return;	etr_disable_sync_clock(NULL);	set_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events);	schedule_work(&etr_work);}/* * ETR sync check machine check. This is called when the ETR OTE and the * local clock OTE are farther apart than the ETR sync check tolerance. * After a ETR sync check the clock is not in sync. The machine check * is broadcasted to all cpus at the same time. */void etr_sync_check(void){	if (!etr_eacr.es)		return;	etr_disable_sync_clock(NULL);	set_bit(ETR_EVENT_SYNC_CHECK, &etr_events);	schedule_work(&etr_work);}/* * ETR external interrupt. There are two causes: * 1) port state change, check the usability of the port * 2) port alert, one of the ETR-data-validity bits (v1-v2 bits of the *    sldr-status word) or ETR-data word 1 (edf1) or ETR-data word 3 (edf3) *    or ETR-data word 4 (edf4) has changed. */static void etr_ext_handler(__u16 code){	struct etr_interruption_parameter *intparm =		(struct etr_interruption_parameter *) &S390_lowcore.ext_params;	if (intparm->pc0)		/* ETR port 0 state change. */		set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events);	if (intparm->pc1)		/* ETR port 1 state change. */		set_bit(ETR_EVENT_PORT1_CHANGE, &etr_events);	if (intparm->eai)		/*		 * ETR port alert on either port 0, 1 or both.		 * Both ports are not up-to-date now.		 */		set_bit(ETR_EVENT_PORT_ALERT, &etr_events);	schedule_work(&etr_work);}static void etr_timeout(unsigned long dummy){	set_bit(ETR_EVENT_UPDATE, &etr_events);	schedule_work(&etr_work);}/* * Check if the etr mode is pss. */static inline int etr_mode_is_pps(struct etr_eacr eacr){	return eacr.es && !eacr.sl;}/* * Check if the etr mode is etr. */static inline int etr_mode_is_etr(struct etr_eacr eacr){	return eacr.es && eacr.sl;}/* * Check if the port can be used for TOD synchronization. * For PPS mode the port has to receive OTEs. For ETR mode * the port has to receive OTEs, the ETR stepping bit has to * be zero and the validity bits for data frame 1, 2, and 3 * have to be 1. */static int etr_port_valid(struct etr_aib *aib, int port){	unsigned int psc;	/* Check that this port is receiving OTEs. */	if (aib->tsp == 0)		return 0;	psc = port ? aib->esw.psc1 : aib->esw.psc0;	if (psc == etr_lpsc_pps_mode)		return 1;	if (psc == etr_lpsc_operational_step)		return !aib->esw.y && aib->slsw.v1 &&			aib->slsw.v2 && aib->slsw.v3;	return 0;}/* * Check if two ports are on the same network. */static int etr_compare_network(struct etr_aib *aib1, struct etr_aib *aib2){	// FIXME: any other fields we have to compare?	return aib1->edf1.net_id == aib2->edf1.net_id;}/* * Wrapper for etr_stei that converts physical port states * to logical port states to be consistent with the output * of stetr (see etr_psc vs. etr_lpsc). */static void etr_steai_cv(struct etr_aib *aib, unsigned int func){	BUG_ON(etr_steai(aib, func) != 0);	/* Convert port state to logical port state. */	if (aib->esw.psc0 == 1)		aib->esw.psc0 = 2;	else if (aib->esw.psc0 == 0 && aib->esw.p == 0)		aib->esw.psc0 = 1;	if (aib->esw.psc1 == 1)		aib->esw.psc1 = 2;	else if (aib->esw.psc1 == 0 && aib->esw.p == 1)		aib->esw.psc1 = 1;}/* * Check if the aib a2 is still connected to the same attachment as * aib a1, the etv values differ by one and a2 is valid. */static int etr_aib_follows(struct etr_aib *a1, struct etr_aib *a2, int p){	int state_a1, state_a2;	/* Paranoia check: e0/e1 should better be the same. */	if (a1->esw.eacr.e0 != a2->esw.eacr.e0 ||	    a1->esw.eacr.e1 != a2->esw.eacr.e1)		return 0;	/* Still connected to the same etr ? */	state_a1 = p ? a1->esw.psc1 : a1->esw.psc0;	state_a2 = p ? a2->esw.psc1 : a2->esw.psc0;	if (state_a1 == etr_lpsc_operational_step) {		if (state_a2 != etr_lpsc_operational_step ||		    a1->edf1.net_id != a2->edf1.net_id ||		    a1->edf1.etr_id != a2->edf1.etr_id ||		    a1->edf1.etr_pn != a2->edf1.etr_pn)			return 0;	} else if (state_a2 != etr_lpsc_pps_mode)		return 0;	/* The ETV value of a2 needs to be ETV of a1 + 1. */	if (a1->edf2.etv + 1 != a2->edf2.etv)		return 0;	if (!etr_port_valid(a2, p))		return 0;	return 1;}/* * The time is "clock". xtime is what we think the time is. * Adjust the value by a multiple of jiffies and add the delta to ntp. * "delay" is an approximation how long the synchronization took. If * the time correction is positive, then "delay" is subtracted from * the time difference and only the remaining part is passed to ntp. */static void etr_adjust_time(unsigned long long clock, unsigned long long delay){	unsigned long long delta, ticks;	struct timex adjust;	/*	 * We don't have to take the xtime lock because the cpu	 * executing etr_adjust_time is running disabled in	 * tasklet context and all other cpus are looping in	 * etr_sync_cpu_start.	 */	if (clock > xtime_cc) {		/* It is later than we thought. */		delta = ticks = clock - xtime_cc;		delta = ticks = (delta < delay) ? 0 : delta - delay;		delta -= do_div(ticks, CLK_TICKS_PER_JIFFY);		init_timer_cc = init_timer_cc + delta;		jiffies_timer_cc = jiffies_timer_cc + delta;		xtime_cc = xtime_cc + delta;		adjust.offset = ticks * (1000000 / HZ);	} else {		/* It is earlier than we thought. */		delta = ticks = xtime_cc - clock;		delta -= do_div(ticks, CLK_TICKS_PER_JIFFY);		init_timer_cc = init_timer_cc - delta;		jiffies_timer_cc = jiffies_timer_cc - delta;		xtime_cc = xtime_cc - delta;		adjust.offset = -ticks * (1000000 / HZ);	}	if (adjust.offset != 0) {		printk(KERN_NOTICE "etr: time adjusted by %li micro-seconds\n",		       adjust.offset);		adjust.modes = ADJ_OFFSET_SINGLESHOT;		do_adjtimex(&adjust);	}}#ifdef CONFIG_SMPstatic void etr_sync_cpu_start(void *dummy){	int *in_sync = dummy;	etr_enable_sync_clock();	/*	 * This looks like a busy wait loop but it isn't. etr_sync_cpus	 * is called on all other cpus while the TOD clocks is stopped.	 * __udelay will stop the cpu on an enabled wait psw until the	 * TOD is running again.	 */	while (*in_sync == 0) {		__udelay(1);		/*		 * A different cpu changes *in_sync. Therefore use		 * barrier() to force memory access.		 */		barrier();	}	if (*in_sync != 1)		/* Didn't work. Clear per-cpu in sync bit again. */		etr_disable_sync_clock(NULL);	/*	 * This round of TOD syncing is done. Set the clock comparator	 * to the next tick and let the processor continue.	 */	setup_jiffy_timer();}static void etr_sync_cpu_end(void *dummy){}#endif /* CONFIG_SMP *//* * Sync the TOD clock using the port refered to by aibp. This port * has to be enabled and the other port has to be disabled. The * last eacr update has to be more than 1.6 seconds in the past. */static int etr_sync_clock(struct etr_aib *aib, int port){	struct etr_aib *sync_port;	unsigned long long clock, delay;	int in_sync, follows;	int rc;	/* Check if the current aib is adjacent to the sync port aib. */	sync_port = (port == 0) ? &etr_port0 : &etr_port1;	follows = etr_aib_follows(sync_port, aib, port);	memcpy(sync_port, aib, sizeof(*aib));	if (!follows)		return -EAGAIN;	/*	 * Catch all other cpus and make them wait until we have	 * successfully synced the clock. smp_call_function will	 * return after all other cpus are in etr_sync_cpu_start.	 */	in_sync = 0;	preempt_disable();	smp_call_function(etr_sync_cpu_start,&in_sync,0,0);	local_irq_disable();	etr_enable_sync_clock();	/* Set clock to next OTE. */	__ctl_set_bit(14, 21);	__ctl_set_bit(0, 29);	clock = ((unsigned long long) (aib->edf2.etv + 1)) << 32;	if (set_clock(clock) == 0) {		__udelay(1);	/* Wait for the clock to start. */		__ctl_clear_bit(0, 29);		__ctl_clear_bit(14, 21);		etr_stetr(aib);		/* Adjust Linux timing variables. */		delay = (unsigned long long)			(aib->edf2.etv - sync_port->edf2.etv) << 32;		etr_adjust_time(clock, delay);		setup_jiffy_timer();		/* Verify that the clock is properly set. */		if (!etr_aib_follows(sync_port, aib, port)) {			/* Didn't work. */			etr_disable_sync_clock(NULL);			in_sync = -EAGAIN;			rc = -EAGAIN;		} else {			in_sync = 1;			rc = 0;		}	} else {		/* Could not set the clock ?!? */		__ctl_clear_bit(0, 29);		__ctl_clear_bit(14, 21);		etr_disable_sync_clock(NULL);		in_sync = -EAGAIN;		rc = -EAGAIN;	}	local_irq_enable();	smp_call_function(etr_sync_cpu_end,NULL,0,0);	preempt_enable();	return rc;}/* * Handle the immediate effects of the different events. * The port change event is used for online/offline changes. */static struct etr_eacr etr_handle_events(struct etr_eacr eacr){	if (test_and_clear_bit(ETR_EVENT_SYNC_CHECK, &etr_events))		eacr.es = 0;	if (test_and_clear_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events))		eacr.es = eacr.sl = 0;	if (test_and_clear_bit(ETR_EVENT_PORT_ALERT, &etr_events))		etr_port0_uptodate = etr_port1_uptodate = 0;	if (test_and_clear_bit(ETR_EVENT_PORT0_CHANGE, &etr_events)) {		if (eacr.e0)			/*			 * Port change of an enabled port. We have to			 * assume that this can have caused an stepping			 * port switch.			 */			etr_tolec = get_clock();		eacr.p0 = etr_port0_online;		if (!eacr.p0)			eacr.e0 = 0;		etr_port0_uptodate = 0;	}	if (test_and_clear_bit(ETR_EVENT_PORT1_CHANGE, &etr_events)) {		if (eacr.e1)			/*			 * Port change of an enabled port. We have to			 * assume that this can have caused an stepping			 * port switch.			 */			etr_tolec = get_clock();		eacr.p1 = etr_port1_online;		if (!eacr.p1)			eacr.e1 = 0;		etr_port1_uptodate = 0;	}	clear_bit(ETR_EVENT_UPDATE, &etr_events);	return eacr;}/* * Set up a timer that expires after the etr_tolec + 1.6 seconds if * one of the ports needs an update. */static void etr_set_tolec_timeout(unsigned long long now){	unsigned long micros;	if ((!etr_eacr.p0 || etr_port0_uptodate) &&	    (!etr_eacr.p1 || etr_port1_uptodate))		return;	micros = (now > etr_tolec) ? ((now - etr_tolec) >> 12) : 0;	micros = (micros > 1600000) ? 0 : 1600000 - micros;	mod_timer(&etr_timer, jiffies + (micros * HZ) / 1000000 + 1);}/* * Set up a time that expires after 1/2 second. */static void etr_set_sync_timeout(void){	mod_timer(&etr_timer, jiffies + HZ/2);}/* * Update the aib information for one or both ports. */static struct etr_eacr etr_handle_update(struct etr_aib *aib,					 struct etr_eacr eacr){	/* With both ports disabled the aib information is useless. */	if (!eacr.e0 && !eacr.e1)		return eacr;	/* Update port0 or port1 with aib stored in etr_work_fn. */	if (aib->esw.q == 0) {		/* Information for port 0 stored. */		if (eacr.p0 && !etr_port0_uptodate) {			etr_port0 = *aib;			if (etr_port0_online)				etr_port0_uptodate = 1;		}	} else {		/* Information for port 1 stored. */		if (eacr.p1 && !etr_port1_uptodate) {			etr_port1 = *aib;			if (etr_port0_online)				etr_port1_uptodate = 1;

⌨️ 快捷键说明

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