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 }