Annotation of qemu/roms/ipxe/src/net/retry.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * Copyright (C) 2006 Michael Brown <[email protected]>.
                      3:  *
                      4:  * This program is free software; you can redistribute it and/or
                      5:  * modify it under the terms of the GNU General Public License as
                      6:  * published by the Free Software Foundation; either version 2 of the
                      7:  * License, or any later version.
                      8:  *
                      9:  * This program is distributed in the hope that it will be useful, but
                     10:  * WITHOUT ANY WARRANTY; without even the implied warranty of
                     11:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                     12:  * General Public License for more details.
                     13:  *
                     14:  * You should have received a copy of the GNU General Public License
                     15:  * along with this program; if not, write to the Free Software
                     16:  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
                     17:  */
                     18: 
                     19: FILE_LICENCE ( GPL2_OR_LATER );
                     20: 
                     21: #include <stddef.h>
                     22: #include <ipxe/timer.h>
                     23: #include <ipxe/list.h>
                     24: #include <ipxe/process.h>
                     25: #include <ipxe/init.h>
                     26: #include <ipxe/retry.h>
                     27: 
                     28: /** @file
                     29:  *
                     30:  * Retry timers
                     31:  *
                     32:  * A retry timer is a binary exponential backoff timer.  It can be
                     33:  * used to build automatic retransmission into network protocols.
                     34:  *
                     35:  * This implementation of the timer is designed to satisfy RFC 2988
                     36:  * and therefore be usable as a TCP retransmission timer.
                     37:  *
                     38:  * 
                     39:  */
                     40: 
                     41: /* The theoretical minimum that the algorithm in stop_timer() can
                     42:  * adjust the timeout back down to is seven ticks, so set the minimum
                     43:  * timeout to at least that value for the sake of consistency.
                     44:  */
                     45: #define MIN_TIMEOUT 7
                     46: 
                     47: /** List of running timers */
                     48: static LIST_HEAD ( timers );
                     49: 
                     50: /**
                     51:  * Start timer
                     52:  *
                     53:  * @v timer            Retry timer
                     54:  *
                     55:  * This starts the timer running with the current timeout value.  If
                     56:  * stop_timer() is not called before the timer expires, the timer will
                     57:  * be stopped and the timer's callback function will be called.
                     58:  */
                     59: void start_timer ( struct retry_timer *timer ) {
                     60:        if ( ! timer->running ) {
                     61:                list_add ( &timer->list, &timers );
                     62:                ref_get ( timer->refcnt );
                     63:        }
                     64:        timer->start = currticks();
                     65:        timer->running = 1;
                     66: 
                     67:        /* 0 means "use default timeout" */
                     68:        if ( timer->min_timeout == 0 )
                     69:                timer->min_timeout = DEFAULT_MIN_TIMEOUT;
                     70:        /* We must never be less than MIN_TIMEOUT under any circumstances */
                     71:        if ( timer->min_timeout < MIN_TIMEOUT )
                     72:                timer->min_timeout = MIN_TIMEOUT;
                     73:        /* Honor user-specified minimum timeout */
                     74:        if ( timer->timeout < timer->min_timeout )
                     75:                timer->timeout = timer->min_timeout;
                     76: 
                     77:        DBG2 ( "Timer %p started at time %ld (expires at %ld)\n",
                     78:               timer, timer->start, ( timer->start + timer->timeout ) );
                     79: }
                     80: 
                     81: /**
                     82:  * Start timer with a specified fixed timeout
                     83:  *
                     84:  * @v timer            Retry timer
                     85:  * @v timeout          Timeout, in ticks
                     86:  */
                     87: void start_timer_fixed ( struct retry_timer *timer, unsigned long timeout ) {
                     88:        start_timer ( timer );
                     89:        timer->timeout = timeout;
                     90:        DBG2 ( "Timer %p expiry time changed to %ld\n",
                     91:               timer, ( timer->start + timer->timeout ) );
                     92: }
                     93: 
                     94: /**
                     95:  * Stop timer
                     96:  *
                     97:  * @v timer            Retry timer
                     98:  *
                     99:  * This stops the timer and updates the timer's timeout value.
                    100:  */
                    101: void stop_timer ( struct retry_timer *timer ) {
                    102:        unsigned long old_timeout = timer->timeout;
                    103:        unsigned long now = currticks();
                    104:        unsigned long runtime;
                    105: 
                    106:        /* If timer was already stopped, do nothing */
                    107:        if ( ! timer->running )
                    108:                return;
                    109: 
                    110:        list_del ( &timer->list );
                    111:        runtime = ( now - timer->start );
                    112:        timer->running = 0;
                    113:        DBG2 ( "Timer %p stopped at time %ld (ran for %ld)\n",
                    114:               timer, now, runtime );
                    115: 
                    116:        /* Update timer.  Variables are:
                    117:         *
                    118:         *   r = round-trip time estimate (i.e. runtime)
                    119:         *   t = timeout value (i.e. timer->timeout)
                    120:         *   s = smoothed round-trip time
                    121:         *
                    122:         * By choice, we set t = 4s, i.e. allow for four times the
                    123:         * normal round-trip time to pass before retransmitting.
                    124:         *
                    125:         * We want to smooth according to s := ( 7 s + r ) / 8
                    126:         *
                    127:         * Since we don't actually store s, this reduces to
                    128:         * t := ( 7 t / 8 ) + ( r / 2 )
                    129:         *
                    130:         */
                    131:        if ( timer->count ) {
                    132:                timer->count--;
                    133:        } else {
                    134:                timer->timeout -= ( timer->timeout >> 3 );
                    135:                timer->timeout += ( runtime >> 1 );
                    136:                if ( timer->timeout != old_timeout ) {
                    137:                        DBG ( "Timer %p timeout updated to %ld\n",
                    138:                              timer, timer->timeout );
                    139:                }
                    140:        }
                    141: 
                    142:        ref_put ( timer->refcnt );
                    143: }
                    144: 
                    145: /**
                    146:  * Handle expired timer
                    147:  *
                    148:  * @v timer            Retry timer
                    149:  */
                    150: static void timer_expired ( struct retry_timer *timer ) {
                    151:        int fail;
                    152: 
                    153:        /* Stop timer without performing RTT calculations */
                    154:        DBG2 ( "Timer %p stopped at time %ld on expiry\n",
                    155:               timer, currticks() );
                    156:        assert ( timer->running );
                    157:        list_del ( &timer->list );
                    158:        timer->running = 0;
                    159:        timer->count++;
                    160: 
                    161:        /* Back off the timeout value */
                    162:        timer->timeout <<= 1;
                    163:        if ( timer->max_timeout == 0 ) /* 0 means "use default timeout" */
                    164:                timer->max_timeout = DEFAULT_MAX_TIMEOUT;
                    165:        if ( ( fail = ( timer->timeout > timer->max_timeout ) ) )
                    166:                timer->timeout = timer->max_timeout;
                    167:        DBG ( "Timer %p timeout backed off to %ld\n",
                    168:              timer, timer->timeout );
                    169: 
                    170:        /* Call expiry callback */
                    171:        timer->expired ( timer, fail );
                    172: 
                    173:        ref_put ( timer->refcnt );
                    174: }
                    175: 
                    176: /**
                    177:  * Single-step the retry timer list
                    178:  *
                    179:  * @v process          Retry timer process
                    180:  */
                    181: static void retry_step ( struct process *process __unused ) {
                    182:        struct retry_timer *timer;
                    183:        unsigned long now = currticks();
                    184:        unsigned long used;
                    185: 
                    186:        /* Process at most one timer expiry.  We cannot process
                    187:         * multiple expiries in one pass, because one timer expiring
                    188:         * may end up triggering another timer's deletion from the
                    189:         * list.
                    190:         */
                    191:        list_for_each_entry ( timer, &timers, list ) {
                    192:                used = ( now - timer->start );
                    193:                if ( used >= timer->timeout ) {
                    194:                        timer_expired ( timer );
                    195:                        break;
                    196:                }
                    197:        }
                    198: }
                    199: 
                    200: /** Retry timer process */
                    201: struct process retry_process __permanent_process = {
                    202:        .list = LIST_HEAD_INIT ( retry_process.list ),
                    203:        .step = retry_step,
                    204: };

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.