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 }