blob: a2c4d083f4c86b87668d4ebefd94b6be08fc3284 [file] [log] [blame]
/* timer.c */
/*
Copyright (C) 1992 Ross Biro
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 1, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
The Author may be reached as bir7@leland.stanford.edu or
C/O Department of Mathematics; Stanford University; Stanford, CA 94305
*/
/* $Id: timer.c,v 0.8.4.8 1993/01/23 18:00:11 bir7 Exp $ */
/* $Log: timer.c,v $
* Revision 0.8.4.8 1993/01/23 18:00:11 bir7
* added volatile keyword.
*
* Revision 0.8.4.7 1993/01/22 23:21:38 bir7
* Merged with 99 pl4
*
* Revision 0.8.4.6 1993/01/22 22:58:08 bir7
* Check in for merge with previous .99 pl 4.
*
* Revision 0.8.4.5 1992/12/12 19:25:04 bir7
* cleaned up Log messages.
*
* Revision 0.8.4.4 1992/12/12 01:50:49 bir7
* Fixed timeouts.
*
* Revision 0.8.4.3 1992/12/06 23:29:59 bir7
* Fixed bugs in timeout.
*
* Revision 0.8.4.2 1992/11/10 10:38:48 bir7
* Change free_s to kfree_s and accidently changed free_skb to kfree_skb.
*
* Revision 0.8.4.1 1992/11/10 00:17:18 bir7
* version change only.
*
* Revision 0.8.3.2 1992/11/10 00:14:47 bir7
* Changed malloc to kmalloc and added Id and Log
* */
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/socket.h>
#include <netinet/in.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <asm/system.h>
#include "timer.h"
#include "ip.h"
#include "tcp.h"
#include "sock.h"
#include "arp.h"
#undef TIMER_DEBUG
#ifdef PRINTK
#undef PRINTK
#endif
#ifdef TIMER_DEBUG
#define PRINTK(x) printk x
#else
#define PRINTK(x) /**/
#endif
static volatile struct timer *timer_base=NULL;
unsigned long seq_offset;
void
delete_timer (struct timer *t)
{
struct timer *tm;
PRINTK (("delete_timer (t=%X)\n",t));
if (timer_base == NULL || t == NULL) return;
cli();
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);
}
sti();
return;
}
for (tm = (struct timer *)timer_base;
tm->next != NULL ;
tm=(struct timer *)tm->next)
{
if (tm->next == t)
{
tm->next = t->next;
sti();
return;
}
}
sti();
}
void
reset_timer (struct timer *t)
{
struct timer *tm;
delete_timer (t);
t->when = timer_seq + t->len;
PRINTK (("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"::);
}
/* first see if it goes at the beginning. */
cli();
if (timer_base == NULL)
{
t->next = NULL;
timer_base = t;
timer_table[NET_TIMER].expires = t->when;
timer_active |= 1 << NET_TIMER;
sti();
return;
}
if (before (t->when, timer_base->when))
{
t->next = timer_base;
timer_base = t;
timer_table[NET_TIMER].expires = t->when;
timer_active |= 1 << NET_TIMER;
sti();
return;
}
for (tm = (struct timer *)timer_base; ; tm=(struct timer *)tm->next)
{
if (tm->next == NULL || before (t->when,tm->next->when))
{
t->next = tm->next;
tm->next = t;
sti();
return;
}
}
}
void
net_timer (void)
{
volatile struct sock *sk;
/* 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. */
while (timer_base != NULL && after (timer_seq+1 ,timer_base->when))
{
int why;
sk = timer_base->sk;
cli();
if (sk->inuse)
{
sti();
break;
}
sk->inuse = 1;
sti();
why = sk->timeout;
PRINTK (("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 ((struct timer *)timer_base);
}
else
{
sk->timeout = 0;
delete_timer((struct timer *)timer_base);
}
/* 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. */
PRINTK (("possible memory leak. sk = %X\n", sk));
reset_timer ((struct timer *)&sk->time_wait);
sk->inuse = 0;
break;
case TIME_CLOSE: /* we've waited long enough, close the
socket. */
sk->state = TCP_CLOSE;
delete_timer ((struct 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);
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 + 2*sk->rtt))
{
sk->time_wait.len = 2*sk->rtt;
sk->timeout = TIME_WRITE;
reset_timer ((struct timer *)&sk->time_wait);
release_sock (sk);
break;
}
PRINTK (("retransmitting.\n"));
sk->prot->retransmit (sk, 0);
if (sk->retransmits > TCP_RETR1)
{
PRINTK (("timer.c TIME_WRITE time-out 1\n"));
arp_destroy (sk->daddr);
ip_route_check (sk->daddr);
}
if (sk->retransmits > TCP_RETR2)
{
PRINTK (("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 ((struct timer *)&sk->time_wait);
release_sock(sk);
break;
}
else /* sk->state == ... */
{
sk->prot->close (sk,1);
break;
}
}
release_sock (sk);
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)
{
PRINTK (("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)
{
PRINTK (("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->state == */
{
sk->prot->close (sk, 1);
}
break;
}
release_sock (sk);
break;
default:
release_sock(sk);
break;
} /* switch */
} /* while (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;
}
}