Annotation of qemu/roms/ipxe/src/net/retry.c, revision 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.