summaryrefslogtreecommitdiffstats
path: root/net/decnet/dn_timer.c
blob: 1e6bd827224b3d261bb4097b0e2e0dd745517726 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/*
 * DECnet       An implementation of the DECnet protocol suite for the LINUX
 *              operating system.  DECnet is implemented using the  BSD Socket
 *              interface as the means of communication with the user level.
 *
 *              DECnet Socket Timer Functions
 *
 * Author:      Steve Whitehouse <SteveW@ACM.org>
 *
 *
 * Changes:
 *       Steve Whitehouse      : Made keepalive timer part of the same
 *                               timer idea.
 *       Steve Whitehouse      : Added checks for sk->sock_readers
 *       David S. Miller       : New socket locking
 *       Steve Whitehouse      : Timer grabs socket ref.
 */
#include <linux/net.h>
#include <linux/socket.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/timer.h>
#include <linux/spinlock.h>
#include <net/sock.h>
#include <asm/atomic.h>
#include <net/dn.h>

/*
 * Fast timer is for delayed acks (200mS max)
 * Slow timer is for everything else (n * 500mS)
 */

#define FAST_INTERVAL (HZ/5)
#define SLOW_INTERVAL (HZ/2)

static void dn_slow_timer(unsigned long arg);

void dn_start_slow_timer(struct sock *sk)
{
	sk->timer.expires = jiffies + SLOW_INTERVAL;
	sk->timer.function = dn_slow_timer;
	sk->timer.data = (unsigned long)sk;

	add_timer(&sk->timer);
}

void dn_stop_slow_timer(struct sock *sk)
{
	del_timer(&sk->timer);
}

static void dn_slow_timer(unsigned long arg)
{
	struct sock *sk = (struct sock *)arg;
	struct dn_scp *scp = &sk->protinfo.dn;

	sock_hold(sk);
	bh_lock_sock(sk);

	if (sk->lock.users != 0) {
		sk->timer.expires = jiffies + HZ / 10;
		add_timer(&sk->timer);
		goto out;
	}

	/*
	 * The persist timer is the standard slow timer used for retransmits
	 * in both connection establishment and disconnection as well as
	 * in the RUN state. The different states are catered for by changing
	 * the function pointer in the socket. Setting the timer to a value
	 * of zero turns it off. We allow the persist_fxn to turn the
	 * timer off in a permant way by returning non-zero, so that
	 * timer based routines may remove sockets. This is why we have a
	 * sock_hold()/sock_put() around the timer to prevent the socket
	 * going away in the middle.
	 */
	if (scp->persist && scp->persist_fxn) {
		if (scp->persist <= SLOW_INTERVAL) {
			scp->persist = 0;

			if (scp->persist_fxn(sk))
				goto out;
		} else {
			scp->persist -= SLOW_INTERVAL;
		}
	}

	/*
	 * Check for keepalive timeout. After the other timer 'cos if
	 * the previous timer caused a retransmit, we don't need to
	 * do this. scp->stamp is the last time that we sent a packet.
	 * The keepalive function sends a link service packet to the
	 * other end. If it remains unacknowledged, the standard
	 * socket timers will eventually shut the socket down. Each
	 * time we do this, scp->stamp will be updated, thus
	 * we won't try and send another until scp->keepalive has passed
	 * since the last successful transmission.
	 */
	if (scp->keepalive && scp->keepalive_fxn && (scp->state == DN_RUN)) {
		if ((jiffies - scp->stamp) >= scp->keepalive)
			scp->keepalive_fxn(sk);
	}

	sk->timer.expires = jiffies + SLOW_INTERVAL;

	add_timer(&sk->timer);
out:
	bh_unlock_sock(sk);
	sock_put(sk);
}

static void dn_fast_timer(unsigned long arg)
{
	struct sock *sk = (struct sock *)arg;
	struct dn_scp *scp = &sk->protinfo.dn;

	bh_lock_sock(sk);
	if (sk->lock.users != 0) {
		scp->delack_timer.expires = jiffies + HZ / 20;
		add_timer(&scp->delack_timer);
		goto out;
	}

	scp->delack_pending = 0;

	if (scp->delack_fxn)
		scp->delack_fxn(sk);
out:
	bh_unlock_sock(sk);
}

void dn_start_fast_timer(struct sock *sk)
{
	struct dn_scp *scp = &sk->protinfo.dn;

	if (!scp->delack_pending) {
		scp->delack_pending = 1;
		scp->delack_timer.next =
		scp->delack_timer.prev = NULL;
		scp->delack_timer.expires = jiffies + FAST_INTERVAL;
		scp->delack_timer.data = (unsigned long)sk;
		scp->delack_timer.function = dn_fast_timer;
		add_timer(&scp->delack_timer);
	}
}

void dn_stop_fast_timer(struct sock *sk)
{
	struct dn_scp *scp = &sk->protinfo.dn;

	if (scp->delack_pending) {
		scp->delack_pending = 0;
		del_timer(&scp->delack_timer);
	}
}