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 }