| /* |
| * INET An implementation of the TCP/IP protocol suite for the LINUX |
| * operating system. INET is implemented using the BSD Socket |
| * interface as the means of communication with the user level. |
| * |
| * TIMER - implementation of software timers. |
| * |
| * Version: @(#)timer.c 1.0.7 05/25/93 |
| * |
| * Authors: Ross Biro, <bir7@leland.Stanford.Edu> |
| * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> |
| * Corey Minyard <wf-rch!minyard@relay.EU.net> |
| * Fred Baumgarten, <dc6iq@insu1.etec.uni-karlsruhe.de> |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version |
| * 2 of the License, or (at your option) any later version. |
| */ |
| #include <linux/types.h> |
| #include <linux/errno.h> |
| #include <linux/socket.h> |
| #include <linux/in.h> |
| #include <linux/kernel.h> |
| #include <linux/sched.h> |
| #include <linux/timer.h> |
| #include <asm/system.h> |
| #include <linux/interrupt.h> |
| #include "inet.h" |
| #include "timer.h" |
| #include "dev.h" |
| #include "ip.h" |
| #include "protocol.h" |
| #include "tcp.h" |
| #include "skbuff.h" |
| #include "sock.h" |
| #include "arp.h" |
| |
| |
| struct timer *timer_base = NULL; |
| unsigned long seq_offset; |
| |
| |
| void |
| delete_timer(struct timer *t) |
| { |
| struct timer *tm; |
| |
| DPRINTF((DBG_TMR, "delete_timer(t=%X)\n", t)); |
| cli(); |
| if (timer_base == NULL || t == NULL) { |
| t->running = 9; |
| sti(); |
| return; |
| } |
| if (t == timer_base) { |
| timer_base = t->next; |
| if (timer_base != NULL) { |
| timer_table[NET_TIMER].expires = timer_base->when; |
| timer_active |= (1 << NET_TIMER); |
| } else { |
| timer_active &= ~(1 << NET_TIMER); |
| } |
| t->running = 0; |
| sti(); |
| return; |
| } |
| tm = timer_base; |
| while (tm != NULL) { |
| if (tm->next == t) { |
| tm->next = t->next; |
| t->running = 0; |
| sti(); |
| return; |
| } |
| tm = tm->next; |
| } |
| sti(); |
| t->running = 9; |
| } |
| |
| |
| void |
| reset_timer(struct timer *t) |
| { |
| struct timer *tm; |
| |
| DPRINTF((DBG_TMR, "reset_timer(t=%X) when = %d jiffies = %d\n", |
| t, t->when, jiffies)); |
| if (t == NULL) { |
| printk("*** reset timer NULL timer\n"); |
| __asm__ ("\t int $3\n"::); |
| } |
| if (t->running) { |
| DPRINTF((DBG_TMR, "t->running has value of %d, len %d\n", |
| t->running, t->len)); |
| } |
| t->running = 1; |
| delete_timer(t); /* here is another race condition ! */ |
| cli(); /* what about a new reset_timer while being\ */ |
| if (!((t->running == 0) || (t->running == 9))) { /* about to install on old one ? -FB */ |
| printk("reset_timer(): t->running after delete_timer: %d !\n", |
| t->running); |
| sti(); |
| return; |
| } |
| t->running = 2; |
| if ((int) t->len < 0) /* prevent close to infinite timers. THEY _DO_ */ |
| t->len = 3; /* happen (negative values ?) - don't ask me why ! -FB */ |
| |
| delete_timer(t); |
| t->when = timer_seq + t->len; |
| |
| /* First see if it goes at the beginning. */ |
| |
| if (timer_base == NULL) { |
| t->next = NULL; |
| timer_base = t; |
| timer_table[NET_TIMER].expires = timer_base->when; |
| timer_active |= (1 << NET_TIMER); |
| t->running = 3; |
| sti(); |
| return; |
| } |
| if (before(t->when, timer_base->when)) { |
| t->next = timer_base; |
| timer_base = t; |
| timer_table[NET_TIMER].expires = timer_base->when; |
| timer_active |= (1 << NET_TIMER); |
| t->running = 4; |
| |
| sti(); |
| return; |
| } |
| tm = timer_base; |
| while (tm != NULL) { |
| if (tm->next == NULL || t->when < tm->next->when) { |
| t->next = tm->next; |
| tm->next = t; |
| timer_table[NET_TIMER].expires = timer_base->when; |
| timer_active |= (1 << NET_TIMER); |
| t->running = 5; |
| sti(); |
| return; |
| } |
| tm = tm->next; |
| } |
| sti(); |
| } |
| |
| |
| /* |
| * Now we will only be called whenever we need to do |
| * something, but we must be sure to process all of the |
| * sockets that need it. |
| */ |
| void |
| net_timer(void) |
| { |
| struct sock *sk; |
| struct timer *tm; |
| |
| cli(); /* a timer might expire and a new one with */ |
| /* earlier expiration could be inserted before -FB */ |
| tm = timer_base; |
| while ((tm != NULL) && (jiffies >= tm->when)) { |
| int why; |
| |
| sk = tm->sk; |
| if (sk->inuse) { |
| break; |
| } |
| sk->inuse = 1; |
| sti(); |
| why = sk->timeout; |
| |
| DPRINTF((DBG_TMR, "net_timer: found sk=%X why = %d\n", sk, why)); |
| if (sk->keepopen) { |
| sk->time_wait.len = TCP_TIMEOUT_LEN; |
| sk->timeout = TIME_KEEPOPEN; |
| reset_timer(tm); |
| } else { |
| sk->timeout = 0; |
| delete_timer(tm); |
| } |
| |
| /* Always see if we need to send an ack. */ |
| if (sk->ack_backlog) { |
| sk->prot->read_wakeup(sk); |
| if (!sk->dead) wake_up(sk->sleep); |
| } |
| |
| /* Now we need to figure out why the socket was on the timer. */ |
| switch (why) { |
| case TIME_DONE: |
| if (!sk->dead || sk->state != TCP_CLOSE) { |
| printk("non dead socket in time_done\n"); |
| release_sock(sk); |
| break; |
| } |
| destroy_sock(sk); |
| break; |
| case TIME_DESTROY: |
| /* |
| * We've waited for a while for all the memory |
| * assosiated with the socket to be freed. We |
| * need to print an error message. |
| */ |
| DPRINTF((DBG_TMR, "possible memory leak. sk = %X\n", sk)); |
| destroy_sock(sk); |
| sk->inuse = 0; |
| break; |
| case TIME_CLOSE: |
| /* We've waited long enough, close the socket. */ |
| sk->state = TCP_CLOSE; |
| delete_timer(&sk->time_wait); |
| |
| /* |
| * Kill the ARP entry in case the hardware |
| * has changed. |
| */ |
| arp_destroy(sk->daddr); |
| if (!sk->dead) wake_up(sk->sleep); |
| sk->shutdown = SHUTDOWN_MASK; |
| sk->time_wait.len = TCP_DONE_TIME; |
| sk->timeout = TIME_DESTROY; |
| reset_timer (&sk->time_wait); |
| release_sock(sk); |
| break; |
| case TIME_WRITE: /* try to retransmit. */ |
| /* |
| * It could be we got here because we |
| * needed to send an ack. So we need |
| * to check for that. |
| */ |
| if (sk->send_head != NULL) { |
| if (before(jiffies, sk->send_head->when + |
| sk->rtt)) { |
| sk->time_wait.len = sk->rtt; |
| sk->timeout = TIME_WRITE; |
| reset_timer(&sk->time_wait); |
| release_sock(sk); |
| break; |
| } |
| DPRINTF((DBG_TMR, "retransmitting.\n")); |
| sk->prot->retransmit(sk, 0); |
| |
| if (sk->retransmits > TCP_RETR1) { |
| DPRINTF((DBG_TMR, "timer.c TIME_WRITE time-out 1\n")); |
| arp_destroy(sk->daddr); |
| ip_route_check(sk->daddr); |
| } |
| |
| if (sk->retransmits > TCP_RETR2) { |
| DPRINTF((DBG_TMR, "timer.c TIME_WRITE time-out 2\n")); |
| sk->err = ETIMEDOUT; |
| if (sk->state == TCP_FIN_WAIT1 || |
| sk->state == TCP_FIN_WAIT2 || |
| sk->state == TCP_LAST_ACK) { |
| sk->state = TCP_TIME_WAIT; |
| sk->timeout = TIME_CLOSE; |
| sk->time_wait.len = |
| TCP_TIMEWAIT_LEN; |
| reset_timer(&sk->time_wait); |
| } else { |
| sk->prot->close(sk,1); |
| break; |
| } |
| } |
| } |
| release_sock(sk); |
| break; |
| case TIME_KEEPOPEN: |
| /* Send something to keep the connection open. */ |
| if (sk->prot->write_wakeup != NULL) |
| sk->prot->write_wakeup(sk); |
| sk->retransmits ++; |
| if (sk->shutdown == SHUTDOWN_MASK) { |
| sk->prot->close(sk,1); |
| sk->state = TCP_CLOSE; |
| } |
| |
| if (sk->retransmits > TCP_RETR1) { |
| DPRINTF((DBG_TMR, "timer.c TIME_KEEPOPEN time-out 1\n")); |
| arp_destroy(sk->daddr); |
| ip_route_check(sk->daddr); |
| release_sock(sk); |
| break; |
| } |
| if (sk->retransmits > TCP_RETR2) { |
| DPRINTF((DBG_TMR, "timer.c TIME_KEEPOPEN time-out 2\n")); |
| arp_destroy (sk->daddr); |
| sk->err = ETIMEDOUT; |
| if (sk->state == TCP_FIN_WAIT1 || |
| sk->state == TCP_FIN_WAIT2) { |
| sk->state = TCP_TIME_WAIT; |
| if (!sk->dead) wake_up(sk->sleep); |
| release_sock(sk); |
| } else { |
| sk->prot->close (sk, 1); |
| } |
| break; |
| } |
| release_sock(sk); |
| break; |
| default: |
| printk("net timer expired - reason unknown, sk=%08X\n", |
| (int) sk); |
| release_sock(sk); |
| break; |
| } |
| cli(); |
| tm = timer_base; |
| } |
| |
| /* Now we need to reset the timer. */ |
| |
| if (timer_base != NULL) { |
| timer_table[NET_TIMER].expires = timer_base->when; |
| timer_active |= 1 << NET_TIMER; |
| } |
| sti(); |
| } |