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/strsun.h>
37 #include <sys/strsubr.h>
38 #include <sys/squeue_impl.h>
39 #include <sys/squeue.h>
40 #include <sys/callo.h>
41
42 #include <inet/common.h>
43 #include <inet/ip.h>
44 #include <inet/ip_ire.h>
45 #include <inet/ip_rts.h>
46 #include <inet/dccp_impl.h>
47
48 #include <sys/cmn_err.h>
49
50 kmem_cache_t *dccp_timercache;
51
52 static void dccp_timer_callback(void *);
53 static void dccp_timer_handler(void *, mblk_t *, void *, ip_recv_attr_t *);
54 static void dccp_timer_free(dccp_t *, mblk_t *);
55
56 /*
57 * Allocate a new timer.
58 */
59 timeout_id_t
60 dccp_timeout(conn_t *connp, void (*f)(void *), hrtime_t tim)
61 {
62 dccp_t *dccp = connp->conn_dccp;
63 dccp_timer_t *dccpt;
64 mblk_t *mp;
65
66 cmn_err(CE_NOTE, "dccp_timers.c: dccp_timeout");
67
68 ASSERT(connp->conn_sqp != NULL);
69
70 if (dccp->dccp_timercache == NULL) {
71 mp = dccp_timermp_alloc(KM_NOSLEEP | KM_PANIC);
72 } else {
73 mp = dccp->dccp_timercache;
74 dccp->dccp_timercache = mp->b_next;
75 mp->b_next = NULL;
76 ASSERT(mp->b_wptr == NULL);
77 }
78
79 CONN_INC_REF(connp);
80 dccpt = (dccp_timer_t *)mp->b_rptr;
81 dccpt->connp = connp;
82 dccpt->dccpt_proc = f;
83 dccpt->dccpt_tid = timeout_generic(CALLOUT_NORMAL, dccp_timer_callback,
84 mp, tim * MICROSEC, CALLOUT_TCP_RESOLUTION, CALLOUT_FLAG_ROUNDUP);
85 /* CALLOUT_DCCP_RESOLUTION */
86 VERIFY(!(dccpt->dccpt_tid & CALLOUT_ID_FREE));
87
88 return ((timeout_id_t)mp);
89 }
90
91 /*
92 * Callback function.
93 */
94 static void
95 dccp_timer_callback(void *arg)
96 {
97 conn_t *connp;
98 dccp_timer_t *dccpt;
99 mblk_t *mp = (mblk_t *)arg;
100
101 dccpt = (dccp_timer_t *)mp->b_rptr;
102 connp = dccpt->connp;
103 SQUEUE_ENTER_ONE(connp->conn_sqp, mp, dccp_timer_handler, connp,
104 NULL, SQ_FILL, SQTAG_DCCP_TIMER);
105 }
106
107 /*
108 * Fires the timer callback function.
109 */
110 /* ARGSUSED */
111 static void
112 dccp_timer_handler(void *arg, mblk_t *mp, void *arg2, ip_recv_attr_t *dummy)
113 {
114 conn_t *connp = (conn_t *)arg;
115 dccp_t *dccp = connp->conn_dccp;
116 dccp_timer_t *dccpt;
117
118 cmn_err(CE_NOTE, "dccp_timers.c: dccp_timer_handler");
119
120 dccpt = (dccp_timer_t *)mp->b_rptr;
121
122 ASSERT(connp == dccpt->connp);
123 ASSERT((squeue_t *)arg2 == connp->conn_sqp);
124
125 if (dccpt->dccpt_tid & CALLOUT_ID_FREE) {
126 dccp_timer_free(connp->conn_dccp, mp);
127 return;
128 }
129
130 if (dccp->dccp_state != DCCPS_CLOSED) {
131 (*dccpt->dccpt_proc)(connp);
132 } else {
133 dccp->dccp_timer_tid = 0;
134 }
135
136 dccp_timer_free(connp->conn_dccp, mp);
137 }
138
139 clock_t
140 dccp_timeout_cancel(conn_t *connp, timeout_id_t id)
141 {
142 dccp_timer_t *dccpt;
143 mblk_t *mp = (mblk_t *)id;
144 clock_t delta;
145
146 if (mp == NULL) {
147 return (-1);
148 }
149
150 dccpt = (dccp_timer_t *)mp->b_rptr;
151 ASSERT(dccpt->connp == connp);
152
153 delta = untimeout_default(dccpt->dccpt_tid, 0);
154
155 if (delta >= 0) {
156 dccp_timer_free(connp->conn_dccp, mp);
157 CONN_DEC_REF(connp);
158 } else {
159 dccpt->dccpt_tid |= CALLOUT_ID_FREE;
160 delta = 0;
161 }
162
163 return (TICK_TO_MSEC(delta));
164 }
165
166 /*
167 * Allocate per-dccp timer cache.
168 */
169 mblk_t *
170 dccp_timermp_alloc(int kmflags)
171 {
172 mblk_t *mp;
173
174 mp = (mblk_t *)kmem_cache_alloc(dccp_timercache, kmflags & ~KM_PANIC);
175 if (mp != NULL) {
176 mp->b_next = mp->b_prev = NULL;
177 mp->b_rptr = (uchar_t *)(&mp[1]);
178 mp->b_wptr = NULL;
179 mp->b_datap = NULL;
180 mp->b_queue = NULL;
181 mp->b_cont = NULL;
182 } else if (kmflags & KM_PANIC) {
183 /* XXX */
184 }
185
186 return (mp);
187 }
188
189 /*
190 * Free per-dccp timer cache.
191 */
192 void
193 dccp_timermp_free(dccp_t *dccp)
194 {
195 mblk_t *mp;
196
197 while ((mp = dccp->dccp_timercache) != NULL) {
198 ASSERT(mp->b_wptr == NULL);
199 dccp->dccp_timercache = dccp->dccp_timercache->b_next;
200 kmem_cache_free(dccp_timercache, mp);
201 }
202 }
203
204 /*
205 * Free timer event.
206 */
207 static void
208 dccp_timer_free(dccp_t *dccp, mblk_t *mp)
209 {
210 mblk_t *mp1 = dccp->dccp_timercache;
211
212 if (mp->b_wptr != NULL) {
213 if (mp->b_wptr != (uchar_t *)-1) {
214 freeb(mp);
215 } else {
216 kmem_free(mp, (size_t)mp->b_datap);
217 }
218 } else if (mp1 == NULL || mp1->b_next == NULL) {
219 mp->b_rptr = (uchar_t *)(&mp[1]);
220 mp->b_next = mp1;
221 dccp->dccp_timercache = mp;
222 } else {
223 kmem_cache_free(dccp_timercache, mp);
224 }
225 }
226
227 /*
228 * Timer service routine.
229 */
230 void
231 dccp_timer(void *arg)
232 {
233 conn_t *connp = (conn_t *)arg;
234 dccp_t *dccp = connp->conn_dccp;
235 dccp_stack_t *dccps = dccp->dccp_dccps;
236 mblk_t *mp;
237
238 cmn_err(CE_NOTE, "dccp_timers.c: dccp_timer");
239
240 dccp->dccp_timer_tid = 0;
241 }