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 }