1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Copyright 2012 David Hoeppner. All rights reserved.
29 */
30
31 /*
32 * This file contains function related to setting and deleting timers.
33 */
34
35 #include <sys/types.h>
36 #include <sys/strlog.h>
37 #include <sys/strsun.h>
38 #include <sys/strsubr.h>
39 #include <sys/squeue_impl.h>
40 #include <sys/squeue.h>
41 #include <sys/callo.h>
42
43 #include <inet/common.h>
44 #include <inet/ip.h>
45 #include <inet/ip_ire.h>
46 #include <inet/ip_rts.h>
47 #include <inet/dccp_impl.h>
48
49 #include <sys/cmn_err.h>
50
51 kmem_cache_t *dccp_timercache;
52
53 static void dccp_timer_callback(void *);
54 static void dccp_timer_handler(void *, mblk_t *, void *, ip_recv_attr_t *);
55 static void dccp_timer_free(dccp_t *, mblk_t *);
56
57 /*
58 * Allocate a new timer.
59 */
60 timeout_id_t
61 dccp_timeout(conn_t *connp, void (*f)(void *), hrtime_t tim)
62 {
63 dccp_t *dccp = connp->conn_dccp;
64 dccp_timer_t *dccpt;
65 mblk_t *mp;
66
67 cmn_err(CE_NOTE, "dccp_timers.c: dccp_timeout");
68
69 ASSERT(connp->conn_sqp != NULL);
70
71 if (dccp->dccp_timercache == NULL) {
72 mp = dccp_timermp_alloc(KM_NOSLEEP | KM_PANIC);
73 } else {
74 mp = dccp->dccp_timercache;
75 dccp->dccp_timercache = mp->b_next;
76 mp->b_next = NULL;
77 ASSERT(mp->b_wptr == NULL);
78 }
79
80 CONN_INC_REF(connp);
81 dccpt = (dccp_timer_t *)mp->b_rptr;
82 dccpt->connp = connp;
83 dccpt->dccpt_proc = f;
84 dccpt->dccpt_tid = timeout_generic(CALLOUT_NORMAL, dccp_timer_callback,
85 mp, tim * MICROSEC, CALLOUT_TCP_RESOLUTION, CALLOUT_FLAG_ROUNDUP);
86 /* CALLOUT_DCCP_RESOLUTION */
87 VERIFY(!(dccpt->dccpt_tid & CALLOUT_ID_FREE));
88
89 return ((timeout_id_t)mp);
90 }
91
92 /*
93 * Callback function.
94 */
95 static void
96 dccp_timer_callback(void *arg)
97 {
98 conn_t *connp;
99 dccp_timer_t *dccpt;
100 mblk_t *mp = (mblk_t *)arg;
101
102 dccpt = (dccp_timer_t *)mp->b_rptr;
103 connp = dccpt->connp;
104 SQUEUE_ENTER_ONE(connp->conn_sqp, mp, dccp_timer_handler, connp,
105 NULL, SQ_FILL, SQTAG_DCCP_TIMER);
106 }
107
108 /*
109 * Fires the timer callback function.
110 */
111 /* ARGSUSED */
112 static void
113 dccp_timer_handler(void *arg, mblk_t *mp, void *arg2, ip_recv_attr_t *dummy)
114 {
115 conn_t *connp = (conn_t *)arg;
116 dccp_t *dccp = connp->conn_dccp;
117 dccp_timer_t *dccpt;
118
119 cmn_err(CE_NOTE, "dccp_timers.c: dccp_timer_handler");
120
121 dccpt = (dccp_timer_t *)mp->b_rptr;
122
123 ASSERT(connp == dccpt->connp);
124 ASSERT((squeue_t *)arg2 == connp->conn_sqp);
125
126 if (dccpt->dccpt_tid & CALLOUT_ID_FREE) {
127 dccp_timer_free(connp->conn_dccp, mp);
128 return;
129 }
130
131 if (dccp->dccp_state != DCCPS_CLOSED) {
132 (*dccpt->dccpt_proc)(connp);
133 } else {
134 dccp->dccp_timer_tid = 0;
135 }
136
137 dccp_timer_free(connp->conn_dccp, mp);
138 }
139
140 clock_t
141 dccp_timeout_cancel(conn_t *connp, timeout_id_t id)
142 {
143 dccp_timer_t *dccpt;
144 mblk_t *mp = (mblk_t *)id;
145 clock_t delta;
146
147 if (mp == NULL) {
148 return (-1);
149 }
150
151 dccpt = (dccp_timer_t *)mp->b_rptr;
152 ASSERT(dccpt->connp == connp);
153
154 delta = untimeout_default(dccpt->dccpt_tid, 0);
155
156 if (delta >= 0) {
157 dccp_timer_free(connp->conn_dccp, mp);
158 CONN_DEC_REF(connp);
159 } else {
160 dccpt->dccpt_tid |= CALLOUT_ID_FREE;
161 delta = 0;
162 }
163
164 return (TICK_TO_MSEC(delta));
165 }
166
167 /*
168 * Allocate per-dccp timer cache.
169 */
170 mblk_t *
171 dccp_timermp_alloc(int kmflags)
172 {
173 mblk_t *mp;
174
175 mp = (mblk_t *)kmem_cache_alloc(dccp_timercache, kmflags & ~KM_PANIC);
176 if (mp != NULL) {
177 mp->b_next = mp->b_prev = NULL;
178 mp->b_rptr = (uchar_t *)(&mp[1]);
179 mp->b_wptr = NULL;
180 mp->b_datap = NULL;
181 mp->b_queue = NULL;
182 mp->b_cont = NULL;
183 } else if (kmflags & KM_PANIC) {
184 /* XXX */
185 }
186
187 return (mp);
188 }
189
190 /*
191 * Free per-dccp timer cache.
192 */
193 void
194 dccp_timermp_free(dccp_t *dccp)
195 {
196 mblk_t *mp;
197
198 while ((mp = dccp->dccp_timercache) != NULL) {
199 ASSERT(mp->b_wptr == NULL);
200 dccp->dccp_timercache = dccp->dccp_timercache->b_next;
201 kmem_cache_free(dccp_timercache, mp);
202 }
203 }
204
205 /*
206 * Free timer event.
207 */
208 static void
209 dccp_timer_free(dccp_t *dccp, mblk_t *mp)
210 {
211 mblk_t *mp1 = dccp->dccp_timercache;
212
213 if (mp->b_wptr != NULL) {
214 if (mp->b_wptr != (uchar_t *)-1) {
215 freeb(mp);
216 } else {
217 kmem_free(mp, (size_t)mp->b_datap);
218 }
219 } else if (mp1 == NULL || mp1->b_next == NULL) {
220 mp->b_rptr = (uchar_t *)(&mp[1]);
221 mp->b_next = mp1;
222 dccp->dccp_timercache = mp;
223 } else {
224 kmem_cache_free(dccp_timercache, mp);
225 }
226 }
227
228 /*
229 * Stop all DCCP timers.
230 */
231 void
232 dccp_timers_stop(dccp_t *dccp)
233 {
234 if (dccp->dccp_timer_tid != 0) {
235 (void) DCCP_TIMER_CANCEL(dccp, dccp->dccp_timer_tid);
236 dccp->dccp_timer_tid = 0;
237 }
238
239 if (dccp->dccp_ka_tid != 0) {
240 (void) DCCP_TIMER_CANCEL(dccp, dccp->dccp_ka_tid);
241 dccp->dccp_ka_tid = 0;
242 }
243 }
244
245 /*
246 * Keepalive timer.
247 */
248 void
249 dccp_keepalive_timer(void *arg)
250 {
251 conn_t *connp = (conn_t *)arg;
252 dccp_t *dccp = connp->conn_dccp;
253 dccp_stack_t *dccps = dccp->dccp_dccps;
254 int32_t firetime;
255 int32_t idletime;
256 int32_t ka_intrvl;
257
258 cmn_err(CE_NOTE, "dccp_timers.c: dccp_keepalive_timer");
259
260 dccp->dccp_ka_tid = 0;
261
262 DCCPS_BUMP_MIB(dccps, dccpTimKeepalive);
263 ka_intrvl = dccp->dccp_ka_interval;
264
265 if (dccp->dccp_state > DCCPS_CLOSING) {
266 return;
267 }
268
269 if (dccp->dccp_state < DCCPS_OPEN) {
270 dccp->dccp_ka_tid = DCCP_TIMER(dccp, dccp_keepalive_timer,
271 ka_intrvl);
272 return;
273 }
274
275 idletime = TICK_TO_MSEC(ddi_get_lbolt() - dccp->dccp_last_recv_time);
276
277 /* XXX */
278
279 if ((firetime = ka_intrvl - idletime) < 0) {
280 firetime = ka_intrvl;
281 }
282
283 dccp->dccp_ka_tid = DCCP_TIMER(dccp, dccp_keepalive_timer, firetime);
284 }
285
286 /*
287 * Timer service routine.
288 */
289 void
290 dccp_timer(void *arg)
291 {
292 conn_t *connp = (conn_t *)arg;
293 dccp_t *dccp = connp->conn_dccp;
294 dccp_stack_t *dccps = dccp->dccp_dccps;
295 mblk_t *mp;
296 clock_t first_threshold;
297 clock_t second_threshold;
298 clock_t ms;
299 uint32_t mss;
300 boolean_t dont_timeout = B_FALSE;
301
302 cmn_err(CE_NOTE, "dccp_timers.c: dccp_timer");
303
304 dccp->dccp_timer_tid = 0;
305
306 first_threshold = dccp->dccp_first_timer_threshold;
307 second_threshold = dccp->dccp_second_timer_threshold;
308 switch (dccp->dccp_state) {
309 case DCCPS_REQUEST:
310 first_threshold = dccp->dccp_first_ctimer_threshold;
311 second_threshold = dccp->dccp_second_ctimer_threshold;
312
313 if (second_threshold == 0) {
314 second_threshold = dccps->dccps_ip_abort_linterval;
315 if (dccp->dccp_active_open) {
316 dont_timeout = B_TRUE;
317 }
318 }
319 break;
320 case DCCPS_CLOSING:
321 (void) dccp_clean_death(dccp, 0);
322 return;
323 default:
324 if (connp->conn_debug) {
325 (void) strlog(DCCP_MOD_ID, 0, 1, SL_TRACE|SL_ERROR,
326 "dccp_timer: strange state (%d) %s",
327 dccp->dccp_state, dccp_display(dccp, NULL,
328 DISP_PORT_ONLY));
329 }
330 return;
331 }
332
333 ASSERT(second_threshold != 0);
334
335 ms = 900;
336
337 DCCP_TIMER_RESTART(dccp, ms);
338
339 DCCPS_BUMP_MIB(dccps, dccpRetransSegs);
340 }