1/*
2 * DECnet       An implementation of the DECnet protocol suite for the LINUX
3 *              operating system.  DECnet is implemented using the  BSD Socket
4 *              interface as the means of communication with the user level.
5 *
6 *              DECnet Socket Timer Functions
7 *
8 * Author:      Steve Whitehouse <SteveW@ACM.org>
9 *
10 *
11 * Changes:
12 *       Steve Whitehouse      : Made keepalive timer part of the same
13 *                               timer idea.
14 *       Steve Whitehouse      : Added checks for sk->sock_readers
15 *       David S. Miller       : New socket locking
16 *       Steve Whitehouse      : Timer grabs socket ref.
17 */
18#include <linux/net.h>
19#include <linux/socket.h>
20#include <linux/skbuff.h>
21#include <linux/netdevice.h>
22#include <linux/timer.h>
23#include <linux/spinlock.h>
24#include <net/sock.h>
25#include <asm/atomic.h>
26#include <net/dn.h>
27
28/*
29 * Fast timer is for delayed acks (200mS max)
30 * Slow timer is for everything else (n * 500mS)
31 */
32
33#define FAST_INTERVAL (HZ/5)
34#define SLOW_INTERVAL (HZ/2)
35
36static void dn_slow_timer(unsigned long arg);
37
38void dn_start_slow_timer(struct sock *sk)
39{
40	sk->timer.expires = jiffies + SLOW_INTERVAL;
41	sk->timer.function = dn_slow_timer;
42	sk->timer.data = (unsigned long)sk;
43
44	add_timer(&sk->timer);
45}
46
47void dn_stop_slow_timer(struct sock *sk)
48{
49	del_timer(&sk->timer);
50}
51
52static void dn_slow_timer(unsigned long arg)
53{
54	struct sock *sk = (struct sock *)arg;
55	struct dn_scp *scp = DN_SK(sk);
56
57	sock_hold(sk);
58	bh_lock_sock(sk);
59
60	if (sk->lock.users != 0) {
61		sk->timer.expires = jiffies + HZ / 10;
62		add_timer(&sk->timer);
63		goto out;
64	}
65
66	/*
67	 * The persist timer is the standard slow timer used for retransmits
68	 * in both connection establishment and disconnection as well as
69	 * in the RUN state. The different states are catered for by changing
70	 * the function pointer in the socket. Setting the timer to a value
71	 * of zero turns it off. We allow the persist_fxn to turn the
72	 * timer off in a permant way by returning non-zero, so that
73	 * timer based routines may remove sockets. This is why we have a
74	 * sock_hold()/sock_put() around the timer to prevent the socket
75	 * going away in the middle.
76	 */
77	if (scp->persist && scp->persist_fxn) {
78		if (scp->persist <= SLOW_INTERVAL) {
79			scp->persist = 0;
80
81			if (scp->persist_fxn(sk))
82				goto out;
83		} else {
84			scp->persist -= SLOW_INTERVAL;
85		}
86	}
87
88	/*
89	 * Check for keepalive timeout. After the other timer 'cos if
90	 * the previous timer caused a retransmit, we don't need to
91	 * do this. scp->stamp is the last time that we sent a packet.
92	 * The keepalive function sends a link service packet to the
93	 * other end. If it remains unacknowledged, the standard
94	 * socket timers will eventually shut the socket down. Each
95	 * time we do this, scp->stamp will be updated, thus
96	 * we won't try and send another until scp->keepalive has passed
97	 * since the last successful transmission.
98	 */
99	if (scp->keepalive && scp->keepalive_fxn && (scp->state == DN_RUN)) {
100		if ((jiffies - scp->stamp) >= scp->keepalive)
101			scp->keepalive_fxn(sk);
102	}
103
104	sk->timer.expires = jiffies + SLOW_INTERVAL;
105
106	add_timer(&sk->timer);
107out:
108	bh_unlock_sock(sk);
109	sock_put(sk);
110}
111
112static void dn_fast_timer(unsigned long arg)
113{
114	struct sock *sk = (struct sock *)arg;
115	struct dn_scp *scp = DN_SK(sk);
116
117	bh_lock_sock(sk);
118	if (sk->lock.users != 0) {
119		scp->delack_timer.expires = jiffies + HZ / 20;
120		add_timer(&scp->delack_timer);
121		goto out;
122	}
123
124	scp->delack_pending = 0;
125
126	if (scp->delack_fxn)
127		scp->delack_fxn(sk);
128out:
129	bh_unlock_sock(sk);
130}
131
132void dn_start_fast_timer(struct sock *sk)
133{
134	struct dn_scp *scp = DN_SK(sk);
135
136	if (!scp->delack_pending) {
137		scp->delack_pending = 1;
138		init_timer(&scp->delack_timer);
139		scp->delack_timer.expires = jiffies + FAST_INTERVAL;
140		scp->delack_timer.data = (unsigned long)sk;
141		scp->delack_timer.function = dn_fast_timer;
142		add_timer(&scp->delack_timer);
143	}
144}
145
146void dn_stop_fast_timer(struct sock *sk)
147{
148	struct dn_scp *scp = DN_SK(sk);
149
150	if (scp->delack_pending) {
151		scp->delack_pending = 0;
152		del_timer(&scp->delack_timer);
153	}
154}
155
156