timeout.c 8.76 KB
Newer Older
1
/*
2 3 4 5
 * Copyright (C) 2013 CERN (www.cern.ch)
 * Author: Alessandro Rubini
 *
 * Released according to the GNU LGPL, version 2.1 or any later version.
6 7
 */
#include <ppsi/ppsi.h>
baujc's avatar
baujc committed
8
#include "include/ppsi/timeout_prot.h"
9

10 11
#define TIMEOUT_MAX_LOG_VALUE 21 /* 2^21 * 1000 =2097152000ms is the maximum value that can be stored in an integer */
#define TIMEOUT_MIN_LOG_VALUE -9 /* 2^-9 = 1ms is the minimum value that can be stored in an integer */
Jean-Claude BAU's avatar
Jean-Claude BAU committed
12 13
#define TIMEOUT_MAX_VALUE_MS  ((1<< TIMEOUT_MAX_LOG_VALUE)*1000)
#define TIMEOUT_MIN_VALUE_MS  (1000>>-(TIMEOUT_MIN_LOG_VALUE))
14

baujc's avatar
baujc committed
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
typedef struct  {
	char *name; /* if name==NULL then the counter is considered as free */
	int ctrlFlag;
} timeOutConfig_t;


static timeOutConfig_t timeOutConfigs[PP_TO_COUNT]= {
		{
				.name="REQUEST",
				.ctrlFlag= TMO_CF_INSTANCE_DEPENDENT | TMO_CF_ALLOW_COMMON_SET,
		},
		{
				.name="SYNC_SEND",
				.ctrlFlag= TMO_CF_INSTANCE_DEPENDENT | TMO_CF_ALLOW_COMMON_SET,
		},
		{
				.name="BMC",
		},
		{
				.name="ANN_RECEIPT",
				.ctrlFlag= TMO_CF_INSTANCE_DEPENDENT | TMO_CF_ALLOW_COMMON_SET,
		},
		{
				.name="ANN_SEND",
				.ctrlFlag= TMO_CF_INSTANCE_DEPENDENT | TMO_CF_ALLOW_COMMON_SET,
		},
		{
				.name="QUAL",
				.ctrlFlag= TMO_CF_INSTANCE_DEPENDENT | TMO_CF_ALLOW_COMMON_SET,
		},
		{
				.name="PROT_STATE",
				.ctrlFlag= TMO_CF_INSTANCE_DEPENDENT,
		},
		{
				.name="IN_STATE",
				.ctrlFlag= TMO_CF_INSTANCE_DEPENDENT,
		},
		{
				.name="GM_BY_BMCA",
				.ctrlFlag= 0,
		},
};

static inline timeOutInstCnt_t *__pp_get_counter(struct pp_instance *ppi, int index) {
	if ( !(timeOutConfigs[index].ctrlFlag & TMO_CF_INSTANCE_DEPENDENT) )
		ppi=INST(GLBS(ppi),0);
	return &ppi->tmo_cfg[index];
}

void pp_timeout_disable_all(struct pp_instance *ppi) {
	int i;

	for ( i=0; i < PP_TO_COUNT; i++) {
		ppi->tmo_cfg[i].initValueMs=TIMEOUT_DISABLE_VALUE;
		ppi->tmo_cfg[i].tmo=0;
	}
}

/* Return counter index or -1 if not available */
int pp_timeout_get_timer(struct pp_instance *ppi, char *name, to_rand_t rand, int ctl_flags) {
	int i;
	timeOutInstCnt_t *tmoCnt;
	timeOutConfig_t *cfg=&timeOutConfigs[0];
	for ( i=0; i < PP_TO_COUNT; i++) {
		if ( !cfg->name ) {
			cfg->name=name;
			cfg->ctrlFlag=ctl_flags;
			tmoCnt= __pp_get_counter(ppi,i);
			tmoCnt->which_rand=rand;
			ppi->tmo_cfg[i].initValueMs=TIMEOUT_DISABLE_VALUE;
			return i;
		}
		cfg++;
	}
	pp_diag(ppi, time, 1, "No free timer for %s\n",name);
	return -1;
}

void pp_timeout_free_timer(struct pp_instance *ppi, int index){
	if ( index >= PP_TO_PREDEF_COUNTERS )
		timeOutConfigs[index].name=NULL;
}

99 100 101 102 103 104 105
int pp_timeout_log_to_ms ( Integer8 logValue) {
	/* logValue can be in range -128 , +127
	 * However we restrict this range to TIMEOUT_MIN_LOG_VALUE, TIMEOUT_MAX_LOG_VALUE
	 * in order to optimize the calculation
	 */

	if ( logValue >= 0 ) {
Jean-Claude BAU's avatar
Jean-Claude BAU committed
106 107 108
		return ( logValue > TIMEOUT_MAX_LOG_VALUE ) ?
				TIMEOUT_MAX_VALUE_MS :
			    ((1<< logValue)*1000);
109 110
	}
	else {
Jean-Claude BAU's avatar
Jean-Claude BAU committed
111 112 113
		return (logValue<TIMEOUT_MIN_LOG_VALUE) ?
				TIMEOUT_MIN_VALUE_MS :
				(1000>>-logValue);
114 115 116
	}
}

117
/* Init fills the timeout values */
118 119
void pp_timeout_init(struct pp_instance *ppi)
{
120
	portDS_t *port = ppi->portDS;
baujc's avatar
baujc committed
121
	timeOutInstCnt_t *tmoCnt=ppi->tmo_cfg;
baujc's avatar
baujc committed
122
	Boolean p2p=is_delayMechanismP2P(ppi);
123
	Integer8 logDelayRequest=p2p ?
124
			port->logMinPdelayReqInterval : port->logMinDelayReqInterval;
125

baujc's avatar
baujc committed
126
	pp_diag(ppi, time, 3, "%s\n",__func__);
Jean-Claude BAU's avatar
Jean-Claude BAU committed
127

baujc's avatar
baujc committed
128 129 130 131 132 133 134
	/* TO_RAND_NONE is the default value */
	tmoCnt[PP_TO_REQUEST].which_rand = p2p ? TO_RAND_NONE : TO_RAND_0_200;
	tmoCnt[PP_TO_SYNC_SEND].which_rand=
			tmoCnt[PP_TO_ANN_SEND].which_rand=TO_RAND_70_130;

	tmoCnt[PP_TO_REQUEST].initValueMs= pp_timeout_log_to_ms(logDelayRequest);
	tmoCnt[PP_TO_SYNC_SEND].initValueMs =  pp_timeout_log_to_ms(port->logSyncInterval);
baujc's avatar
baujc committed
135

baujc's avatar
baujc committed
136 137 138 139 140 141 142 143
	// Initialize BMCA timer (Independent Timer)
	// Take the lowest value of logAnnounceInterval
	{
		timeOutInstCnt_t *gtmoCnt=&INST(GLBS(ppi),0)->tmo_cfg[PP_TO_BMC];
		int ms=pp_timeout_log_to_ms(port->logAnnounceInterval);
		if (gtmoCnt->initValueMs==TIMEOUT_DISABLE_VALUE || ms<gtmoCnt->initValueMs)
			gtmoCnt->initValueMs=ms;
	}
baujc's avatar
baujc committed
144 145 146 147 148
	/* Clause 17.6.5.3 : ExternalPortConfiguration enabled
	 *  - The Announce receipt timeout mechanism (see 9.2.6.12) shall not be active.
	 */
	if ( is_externalPortConfigurationEnabled(DSDEF(ppi)) ) {
		tmoCnt[PP_TO_ANN_RECEIPT].initValueMs =
baujc's avatar
baujc committed
149
				tmoCnt[PP_TO_QUALIFICATION].initValueMs =TIMEOUT_DISABLE_VALUE;
baujc's avatar
baujc committed
150 151 152 153 154
	} else {
		tmoCnt[PP_TO_ANN_RECEIPT].initValueMs=1000 * (port->announceReceiptTimeout << port->logAnnounceInterval);
		tmoCnt[PP_TO_QUALIFICATION].initValueMs =
		    (1000 << port->logAnnounceInterval)*(DSCUR(ppi)->stepsRemoved + 1);
	}
baujc's avatar
baujc committed
155
	tmoCnt[PP_TO_ANN_SEND].initValueMs =  pp_timeout_log_to_ms(port->logAnnounceInterval);
156 157
}

baujc's avatar
baujc committed
158 159 160
int pp_timeout_get(struct pp_instance *ppi, int index) {
	timeOutInstCnt_t *tmoCnt= __pp_get_counter(ppi,index);
	return tmoCnt->initValueMs;
161 162
}

baujc's avatar
baujc committed
163 164 165
static inline void __pp_timeout_set(struct pp_instance *ppi,int index , int millisec) {
	timeOutInstCnt_t *tmoCnt= __pp_get_counter(ppi,index);

166
	tmoCnt->tmo = TOPS(ppi)->calc_timeout(ppi, millisec);
baujc's avatar
baujc committed
167 168 169
}

void pp_timeout_set_rename(struct pp_instance *ppi,int index ,  int millisec, char *name)
170
{
baujc's avatar
baujc committed
171 172 173 174 175 176 177 178
	timeOutInstCnt_t *tmoCnt= __pp_get_counter(ppi,index);

	if ( name!=NULL )
		timeOutConfigs[index].name=name;
	tmoCnt->initValueMs=millisec;
	__pp_timeout_set(ppi,index,millisec);
	pp_diag(ppi, time, 3, "timeout overwr.: %s - %i / %lu\n",
			timeOutConfigs[index].name, millisec, tmoCnt->tmo);
179
}
180

baujc's avatar
baujc committed
181
void __pp_timeout_reset(struct pp_instance *ppi, int index, unsigned int multiplier)
182 183 184
{
	static uint32_t seed;
	int millisec;
baujc's avatar
baujc committed
185
	timeOutInstCnt_t *tmoCnt= __pp_get_counter(ppi,index);
186

baujc's avatar
baujc committed
187 188 189 190
	if ( tmoCnt->initValueMs==TIMEOUT_DISABLE_VALUE)
		return;
	millisec = tmoCnt->initValueMs*multiplier;
	if (tmoCnt->which_rand!=TO_RAND_NONE && millisec>0) {
191
		uint32_t rval;
192

193 194 195 196 197 198 199 200 201 202 203
		if (!seed) {
			uint32_t *p;
			/* use the least 32 bits of the mac address as seed */
			p = (void *)(&DSDEF(ppi)->clockIdentity)
				+ sizeof(ClockIdentity) - 4;
			seed = *p;
		}
		/* From uclibc: they make 11 + 10 + 10 bits, we stop at 21 */
		seed *= 1103515245;
		seed += 12345;
		rval = (unsigned int) (seed / 65536) % 2048;
204

205 206 207 208
		seed *= 1103515245;
		seed += 12345;
		rval <<= 10;
		rval ^= (unsigned int) (seed / 65536) % 1024;
209

210
		millisec=(millisec<<1)/5; /* keep 40% of the reference value */
baujc's avatar
baujc committed
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
		if ( millisec > 0 ) {
			switch(tmoCnt->which_rand) {
			case TO_RAND_70_130:
				/*
				 * We are required to fit between 70% and 130%
				 * of the value for 90% of the time, at least.
				 * So randomize between 80% and 120%: constant
				 * part is 80% and variable is 40%.
				 */
				millisec = (millisec * 2) + rval % millisec;
				break;
			case TO_RAND_0_200:
				millisec = rval % (millisec * 5);
				break;
			default:
			/* RAND_NONE already treated */
				break;
			}
229
		}
230
	}
231
	__pp_timeout_set(ppi, index, millisec);
baujc's avatar
baujc committed
232 233
	pp_diag(ppi, time, 3, "timeout reseted: %s - %i / %lu\n",
			timeOutConfigs[index].name, millisec, tmoCnt->tmo);
234 235
}

236 237 238 239 240 241 242
/*
 * When we enter a new fsm state, we init all timeouts. Who cares if
 * some of them are not used (and even if some have no default timeout)
 */
void pp_timeout_setall(struct pp_instance *ppi)
{
	int i;
baujc's avatar
baujc committed
243 244 245
	for (i = 0; i < PP_TO_COUNT; i++) {
		if (timeOutConfigs[i].ctrlFlag&TMO_CF_ALLOW_COMMON_SET )
			pp_timeout_reset(ppi, i);
246
	}
247 248 249 250
	/* but announce_send must be send soon */
	__pp_timeout_set(ppi, PP_TO_ANN_SEND, 20);
}

251 252
int pp_timeout(struct pp_instance *ppi, int index)
{
baujc's avatar
baujc committed
253 254 255 256 257 258
	timeOutInstCnt_t *tmoCnt= __pp_get_counter(ppi,index);
	int ret;
	unsigned long now;

	if ( tmoCnt->initValueMs==TIMEOUT_DISABLE_VALUE )
		return 0; /* counter is disabled */
259

260
	now=TOPS(ppi)->calc_timeout(ppi, 0);
baujc's avatar
baujc committed
261
	ret = time_after_eq(now,tmoCnt->tmo);
262
	if (ret)
baujc's avatar
baujc committed
263 264
		pp_diag(ppi, time, 1, "timeout expired: %s - %lu\n",
				timeOutConfigs[index].name,now);
265 266 267
	return ret;
}

268 269 270 271 272
/*
 * How many ms to wait for the timeout to happen, for ppi->next_delay.
 * It is not allowed for a timeout to not be pending
 */
int pp_next_delay_1(struct pp_instance *ppi, int i1)
273
{
baujc's avatar
baujc committed
274
	timeOutInstCnt_t *tmoCnt= __pp_get_counter(ppi,i1);
275
	unsigned long now = TOPS(ppi)->calc_timeout(ppi, 0);
276
	signed long r1;
277

baujc's avatar
baujc committed
278
	r1 = tmoCnt->tmo - now;
279
	return r1 < 0 ? 0 : r1;
280 281
}

282 283
int pp_next_delay_2(struct pp_instance *ppi, int i1, int i2)
{
baujc's avatar
baujc committed
284 285
	timeOutInstCnt_t *tmoCnt1= __pp_get_counter(ppi,i1);
	timeOutInstCnt_t *tmoCnt2= __pp_get_counter(ppi,i2);
286
	unsigned long now = TOPS(ppi)->calc_timeout(ppi, 0);
287 288
	signed long r1, r2;

baujc's avatar
baujc committed
289 290
	r1 = tmoCnt1->tmo - now;
	r2 = tmoCnt2->tmo - now;
291 292 293 294 295 296 297
	if (r2 < r1)
		r1 = r2;
	return r1 < 0 ? 0 : r1;
}

int pp_next_delay_3(struct pp_instance *ppi, int i1, int i2, int i3)
{
baujc's avatar
baujc committed
298 299 300
	timeOutInstCnt_t *tmoCnt1= __pp_get_counter(ppi,i1);
	timeOutInstCnt_t *tmoCnt2= __pp_get_counter(ppi,i2);
	timeOutInstCnt_t *tmoCnt3= __pp_get_counter(ppi,i3);
301
	unsigned long now = TOPS(ppi)->calc_timeout(ppi, 0);
302 303
	signed long r1, r2, r3;

baujc's avatar
baujc committed
304 305 306
	r1 =  tmoCnt1->tmo - now;
	r2 =  tmoCnt2->tmo  - now;
	r3 =  tmoCnt3->tmo  - now;
307 308 309 310 311 312
	if (r2 < r1)
		r1 = r2;
	if (r3 < r1)
		r1 = r3;
	return r1 < 0 ? 0 : r1;
}