void ci_ip_timer_state_dump(ci_netif* ni) { ci_ip_timer_state* ipts; ci_ip_timer* ts; ci_ni_dllist_t* bucket; ci_ni_dllist_link* l; ci_iptime_t stime, wheel_base, max_time, min_time; int w, b, bit_shift; /* shifting a 32 bit integer left or right 32 bits has undefined results * (i.e. not 0 which is required). Therefore I now use an array of mask * values */ unsigned wheel_mask[CI_IPTIME_WHEELS] = { WHEEL0_MASK, WHEEL1_MASK, WHEEL2_MASK, 0 }; ipts = IPTIMER_STATE(ni); stime = ipts->sched_ticks; ci_log("%s: time is 0x%x", __FUNCTION__, stime); /* for each wheel */ for(w=0; w < CI_IPTIME_WHEELS; w++) { /* base time of wheel */ wheel_base = stime & wheel_mask[w]; /* for each bucket in wheel */ for (b=0; b < CI_IPTIME_BUCKETS; b++) { /* max and min relative times for this bucket */ bit_shift = CI_IPTIME_BUCKETBITS*w; min_time = wheel_base + (b << bit_shift); max_time = min_time + (1 << bit_shift); bucket = &ipts->warray[w*CI_IPTIME_BUCKETS + b]; /* check buckets that should be empty are! */ if ( TIME_LE(min_time, stime) && !ci_ni_dllist_is_empty(ni, bucket) ) ci_log("w:%d, b:%d, [0x%x->0x%x] - bucket should be empty", w, b, min_time, max_time); /* run through timers in bucket */ for (l = ci_ni_dllist_start(ni, bucket); l != ci_ni_dllist_end(ni, bucket); ci_ni_dllist_iter(ni, l) ) { /* get timer */ ts = LINK2TIMER(l); ci_log(" ts = 0x%x %s w:%d, b:%d, [0x%x->0x%x]", ts->time, ci_ip_timer_dump(ts), w, b, min_time, max_time); if ( TIME_LE(ts->time, stime) ) ci_log(" ERROR: timer before current time"); if ( !(TIME_LT(ts->time, max_time) && TIME_GE(ts->time, min_time)) ) ci_log(" ERROR: timer in wrong bucket"); } } } ci_log("----------------------"); }
void ci_tcp_listenq_insert(ci_netif* ni, ci_tcp_socket_listen* tls, ci_tcp_state_synrecv* tsr) { int is_first; tls->n_listenq++; ci_tcp_listenq_bucket_insert(ni, tls, ci_ni_aux_p2bucket(ni, tls->bucket), tsr, 0); if( OO_SP_NOT_NULL(tsr->local_peer) ) return; is_first = ci_ni_dllist_is_empty(ni, &tls->listenq[0]); ci_ni_dllist_push_tail(ni, &tls->listenq[0], ci_tcp_synrecv2link(tsr)); tsr->retries = 0; tsr->timeout = ci_tcp_time_now(ni) + NI_CONF(ni).tconst_rto_initial; ++tls->n_listenq_new; if( is_first ) ci_tcp_listen_timer_set(ni, tls, tsr->timeout); }
void ci_ip_timer_state_assert_valid(ci_netif* ni, const char* file, int line) { ci_ip_timer_state* ipts; ci_ip_timer* ts; ci_ni_dllist_t* bucket; ci_ni_dllist_link* l; ci_iptime_t stime, wheel_base, max_time, min_time; int a1, a2, a3, w, b, bit_shift; /* shifting a 32 bit integer left or right 32 bits has undefined results * (i.e. not 0 which is required). Therefore I now use an array of mask * values */ unsigned wheel_mask[CI_IPTIME_WHEELS] = { WHEEL0_MASK, WHEEL1_MASK, WHEEL2_MASK, 0 }; ipts = IPTIMER_STATE(ni); stime = ipts->sched_ticks; /* for each wheel */ for(w=0; w < CI_IPTIME_WHEELS; w++) { /* base time of wheel */ wheel_base = stime & wheel_mask[w]; /* for each bucket in wheel */ for (b=0; b < CI_IPTIME_BUCKETS; b++) { /* max and min relative times for this bucket */ bit_shift = CI_IPTIME_BUCKETBITS*w; min_time = wheel_base + (b << bit_shift); max_time = min_time + (1 << bit_shift); bucket = &ipts->warray[w*CI_IPTIME_BUCKETS + b]; /* check list looks valid */ if ( ci_ni_dllist_start(ni, bucket) == ci_ni_dllist_end(ni, bucket) ) { ci_assert( ci_ni_dllist_is_empty(ni, bucket) ); } /* check buckets that should be empty are! */ a3 = TIME_GT(min_time, stime) || ci_ni_dllist_is_empty(ni, bucket); /* run through timers in bucket */ for (l = ci_ni_dllist_start(ni, bucket); l != ci_ni_dllist_end(ni, bucket); ci_ni_dllist_iter(ni, l) ) { ci_ni_dllist_link_assert_valid(ni, l); /* get timer */ ts = LINK2TIMER(l); /* must be in the future */ a1 = TIME_GT(ts->time, stime); /* must be within time range of bucket */ a2 = TIME_LT(ts->time, max_time) && TIME_GE(ts->time, min_time); /* if any of the checks fail then print out timer details */ if (!a1 || !a2 || !a3) { ci_log("%s: [w=0x%x/b=0x%x] stime=0x%x", __FUNCTION__, w, b, stime); ci_log(" --> t=0x%x, min=0x%x, max=0x%x", ts->time, min_time, max_time); ci_log(" [%s line=%d]", file, line); } /* stop if assertion failed */ ci_assert(a1 && a2 && a3); } } } }
/* run any pending timers */ void ci_ip_timer_poll(ci_netif *netif) { ci_ip_timer_state* ipts = IPTIMER_STATE(netif); ci_iptime_t* stime = &ipts->sched_ticks; ci_ip_timer* ts; ci_iptime_t rtime; ci_ni_dllist_link* link; int changed = 0; /* The caller is expected to ensure that the current time is sufficiently ** up-to-date. */ rtime = ci_ip_time_now(netif); /* check for sanity i.e. time always goes forwards */ ci_assert( TIME_GE(rtime, *stime) ); /* bug chasing Bug 2855 - check the temp list used is OK before we start */ ci_assert( ci_ni_dllist_is_valid(netif, &ipts->fire_list.l) ); ci_assert( ci_ni_dllist_is_empty(netif, &ipts->fire_list)); while( TIME_LT(*stime, rtime) ) { DETAILED_CHECK_TIMERS(netif); /* advance the schedulers view of time */ (*stime)++; /* cascade through wheels if reached end of current wheel */ if(BUCKETNO(0, *stime) == 0) { if(BUCKETNO(1, *stime) == 0) { if(BUCKETNO(2, *stime) == 0) { ci_ip_timer_cascadewheel(netif, 3, *stime); } ci_ip_timer_cascadewheel(netif, 2, *stime); } changed = ci_ip_timer_cascadewheel(netif, 1, *stime); } /* Bug 1828: We need to be creaful here ... because: - ci_ip_timer_docallback can set/clear timers - the timers being set/cleared may not necessarily be the ones firing - however, they could be in this bucket In summary, need to ensure the ni_dllist stays valid at all times so safe to call. Slightly complicated by the case that its not possible to hold indirected linked lists on the stack */ ci_assert( ci_ni_dllist_is_valid(netif, &ipts->fire_list.l)); ci_assert( ci_ni_dllist_is_empty(netif, &ipts->fire_list)); /* run timers in the current bucket */ ci_ni_dllist_rehome( netif, &ipts->fire_list, &ipts->warray[BUCKETNO(0, *stime)] ); DETAILED_CHECK_TIMERS(netif); while( (link = ci_ni_dllist_try_pop(netif, &ipts->fire_list)) ) { ts = LINK2TIMER(link); ci_assert_equal(ts->time, *stime); /* ensure time marked as NOT pending */ ci_ni_dllist_self_link(netif, &ts->link); /* callback safe to set/clear this or other timers */ ci_ip_timer_docallback(netif, ts); } ci_assert( ci_ni_dllist_is_valid(netif, &ipts->fire_list.l) ); ci_assert( ci_ni_dllist_is_empty(netif, &ipts->fire_list)); DETAILED_CHECK_TIMERS(netif); } ci_assert( ci_ni_dllist_is_valid(netif, &ipts->fire_list.l) ); ci_assert( ci_ni_dllist_is_empty(netif, &ipts->fire_list)); /* What is our next timer? * Let's update if our previous "closest" timer have already been * handled, or if the previous estimation was "infinity". */ if( TIME_GE(ipts->sched_ticks, ipts->closest_timer) || (changed && ipts->closest_timer - ipts->sched_ticks > IPTIME_INFINITY_LOW) ) { /* we peek into the first wheel only */ ci_iptime_t base = ipts->sched_ticks & WHEEL0_MASK; ci_iptime_t b = ipts->sched_ticks - base; for( b++ ; b < CI_IPTIME_BUCKETS; b++ ) { if( !ci_ni_dllist_is_empty(netif, &ipts->warray[b]) ) { ipts->closest_timer = base + b; return; } } /* We do not know the next timer. Set it to a sort of infinity. */ ipts->closest_timer = ipts->sched_ticks + IPTIME_INFINITY; } }