|
|
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: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.