--- /dev/null	2012-08-18 10:37:11.000000000 +0000
+++ new/usr/src/uts/common/inet/dccp/dccp_timers.c	2012-08-18 10:37:11.160576772 +0000
@@ -0,0 +1,340 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2012 David Hoeppner.  All rights reserved.
+ */
+
+/*
+ * This file contains function related to setting and deleting timers.
+ */
+
+#include <sys/types.h>
+#include <sys/strlog.h>
+#include <sys/strsun.h>
+#include <sys/strsubr.h>
+#include <sys/squeue_impl.h>
+#include <sys/squeue.h>
+#include <sys/callo.h>
+
+#include <inet/common.h>
+#include <inet/ip.h>
+#include <inet/ip_ire.h>
+#include <inet/ip_rts.h>
+#include <inet/dccp_impl.h>
+
+#include <sys/cmn_err.h>
+
+kmem_cache_t	*dccp_timercache;
+
+static void	dccp_timer_callback(void *);
+static void	dccp_timer_handler(void *, mblk_t *, void *, ip_recv_attr_t *);
+static void	dccp_timer_free(dccp_t *, mblk_t *);
+
+/*
+ * Allocate a new timer.
+ */
+timeout_id_t
+dccp_timeout(conn_t *connp, void (*f)(void *), hrtime_t tim)
+{
+	dccp_t		*dccp = connp->conn_dccp;
+	dccp_timer_t	*dccpt;
+	mblk_t		*mp;
+
+	cmn_err(CE_NOTE, "dccp_timers.c: dccp_timeout");
+
+	ASSERT(connp->conn_sqp != NULL);
+
+	if (dccp->dccp_timercache == NULL) {
+		mp = dccp_timermp_alloc(KM_NOSLEEP | KM_PANIC);
+	} else {
+		mp = dccp->dccp_timercache;
+		dccp->dccp_timercache = mp->b_next;
+		mp->b_next = NULL;
+		ASSERT(mp->b_wptr == NULL);
+	}
+
+	CONN_INC_REF(connp);
+	dccpt = (dccp_timer_t *)mp->b_rptr;
+	dccpt->connp = connp;
+	dccpt->dccpt_proc = f;
+	dccpt->dccpt_tid = timeout_generic(CALLOUT_NORMAL, dccp_timer_callback,
+	    mp, tim * MICROSEC, CALLOUT_TCP_RESOLUTION, CALLOUT_FLAG_ROUNDUP);
+	/* CALLOUT_DCCP_RESOLUTION */
+	VERIFY(!(dccpt->dccpt_tid & CALLOUT_ID_FREE));
+
+	return ((timeout_id_t)mp);
+}
+
+/*
+ * Callback function.
+ */
+static void
+dccp_timer_callback(void *arg)
+{
+	conn_t		*connp;
+	dccp_timer_t	*dccpt;
+	mblk_t		*mp = (mblk_t *)arg;
+
+	dccpt = (dccp_timer_t *)mp->b_rptr;
+	connp = dccpt->connp;
+	SQUEUE_ENTER_ONE(connp->conn_sqp, mp, dccp_timer_handler, connp,
+	    NULL, SQ_FILL, SQTAG_DCCP_TIMER);
+}
+
+/*
+ * Fires the timer callback function.
+ */
+/* ARGSUSED */
+static void
+dccp_timer_handler(void *arg, mblk_t *mp, void *arg2, ip_recv_attr_t *dummy)
+{
+	conn_t		*connp = (conn_t *)arg;
+	dccp_t		*dccp = connp->conn_dccp;
+	dccp_timer_t	*dccpt;
+
+	cmn_err(CE_NOTE, "dccp_timers.c: dccp_timer_handler");
+
+	dccpt = (dccp_timer_t *)mp->b_rptr;
+
+	ASSERT(connp == dccpt->connp);
+	ASSERT((squeue_t *)arg2 == connp->conn_sqp);
+
+	if (dccpt->dccpt_tid & CALLOUT_ID_FREE) {
+		dccp_timer_free(connp->conn_dccp, mp);
+		return;
+	}
+
+	if (dccp->dccp_state != DCCPS_CLOSED) {
+		(*dccpt->dccpt_proc)(connp);
+	} else {
+		dccp->dccp_timer_tid = 0;
+	}
+
+	dccp_timer_free(connp->conn_dccp, mp);
+}
+
+clock_t
+dccp_timeout_cancel(conn_t *connp, timeout_id_t id)
+{
+	dccp_timer_t	*dccpt;
+	mblk_t		*mp = (mblk_t *)id;
+	clock_t		delta;
+
+	if (mp == NULL) {
+		return (-1);
+	}
+
+	dccpt = (dccp_timer_t *)mp->b_rptr;
+	ASSERT(dccpt->connp == connp);
+
+	delta = untimeout_default(dccpt->dccpt_tid, 0);
+
+	if (delta >= 0) {
+		dccp_timer_free(connp->conn_dccp, mp);
+		CONN_DEC_REF(connp);
+	} else {
+		dccpt->dccpt_tid |= CALLOUT_ID_FREE;
+		delta = 0;
+	}
+
+	return (TICK_TO_MSEC(delta));
+}
+
+/*
+ * Allocate per-dccp timer cache.
+ */
+mblk_t *
+dccp_timermp_alloc(int kmflags)
+{
+	mblk_t	*mp;
+
+	mp = (mblk_t *)kmem_cache_alloc(dccp_timercache, kmflags & ~KM_PANIC);
+	if (mp != NULL) {
+		mp->b_next = mp->b_prev = NULL;
+		mp->b_rptr = (uchar_t *)(&mp[1]);
+		mp->b_wptr = NULL;
+		mp->b_datap = NULL;
+		mp->b_queue = NULL;
+		mp->b_cont = NULL;
+	} else if (kmflags & KM_PANIC) {
+		/* XXX */
+	}
+
+	return (mp);
+}
+
+/*
+ * Free per-dccp timer cache.
+ */
+void
+dccp_timermp_free(dccp_t *dccp)
+{
+	mblk_t	*mp;
+
+	while ((mp = dccp->dccp_timercache) != NULL) {
+		ASSERT(mp->b_wptr == NULL);
+		dccp->dccp_timercache = dccp->dccp_timercache->b_next;
+		kmem_cache_free(dccp_timercache, mp);
+	}
+}
+
+/*
+ * Free timer event.
+ */
+static void
+dccp_timer_free(dccp_t *dccp, mblk_t *mp)
+{
+	mblk_t	*mp1 = dccp->dccp_timercache;
+
+	if (mp->b_wptr != NULL) {
+		if (mp->b_wptr != (uchar_t *)-1) {
+			freeb(mp);
+		} else {
+			kmem_free(mp, (size_t)mp->b_datap);
+		}
+	} else if (mp1 == NULL || mp1->b_next == NULL) {
+		mp->b_rptr = (uchar_t *)(&mp[1]);
+		mp->b_next = mp1;
+		dccp->dccp_timercache = mp;
+	} else {
+		kmem_cache_free(dccp_timercache, mp);
+	}
+}
+
+/*
+ * Stop all DCCP timers.
+ */
+void
+dccp_timers_stop(dccp_t *dccp)
+{
+	if (dccp->dccp_timer_tid != 0) {
+		(void) DCCP_TIMER_CANCEL(dccp, dccp->dccp_timer_tid);
+		dccp->dccp_timer_tid = 0;
+	}
+
+	if (dccp->dccp_ka_tid != 0) {
+		(void) DCCP_TIMER_CANCEL(dccp, dccp->dccp_ka_tid);
+		dccp->dccp_ka_tid = 0;
+	}
+}
+
+/*
+ * Keepalive timer.
+ */
+void
+dccp_keepalive_timer(void *arg)
+{
+	conn_t		*connp = (conn_t *)arg;
+	dccp_t		*dccp = connp->conn_dccp;
+	dccp_stack_t	*dccps = dccp->dccp_dccps;
+	int32_t		firetime;
+	int32_t		idletime;
+	int32_t		ka_intrvl;
+
+	cmn_err(CE_NOTE, "dccp_timers.c: dccp_keepalive_timer");
+
+	dccp->dccp_ka_tid = 0;
+
+	DCCPS_BUMP_MIB(dccps, dccpTimKeepalive);
+	ka_intrvl = dccp->dccp_ka_interval;
+
+	if (dccp->dccp_state > DCCPS_CLOSING) {
+		return;
+	}
+
+	if (dccp->dccp_state < DCCPS_OPEN) {
+		dccp->dccp_ka_tid = DCCP_TIMER(dccp, dccp_keepalive_timer,
+		    ka_intrvl);
+		return;
+	}
+
+	idletime = TICK_TO_MSEC(ddi_get_lbolt() - dccp->dccp_last_recv_time);
+
+	/* XXX */
+
+	if ((firetime = ka_intrvl - idletime) < 0) {
+		firetime = ka_intrvl;
+	}
+
+	dccp->dccp_ka_tid = DCCP_TIMER(dccp, dccp_keepalive_timer, firetime);
+}
+
+/*
+ * Timer service routine.
+ */
+void
+dccp_timer(void *arg)
+{
+	conn_t		*connp = (conn_t *)arg;
+	dccp_t		*dccp = connp->conn_dccp;
+	dccp_stack_t	*dccps = dccp->dccp_dccps;
+	mblk_t		*mp;
+	clock_t		first_threshold;
+	clock_t		second_threshold;
+	clock_t		ms;
+	uint32_t	mss;
+	boolean_t	dont_timeout = B_FALSE;
+
+	cmn_err(CE_NOTE, "dccp_timers.c: dccp_timer");
+
+	dccp->dccp_timer_tid = 0;
+
+	first_threshold = dccp->dccp_first_timer_threshold;
+	second_threshold = dccp->dccp_second_timer_threshold;
+	switch (dccp->dccp_state) {
+	case DCCPS_REQUEST:
+		first_threshold = dccp->dccp_first_ctimer_threshold;
+		second_threshold = dccp->dccp_second_ctimer_threshold;
+
+		if (second_threshold == 0) {
+			second_threshold = dccps->dccps_ip_abort_linterval;
+			if (dccp->dccp_active_open) {
+				dont_timeout = B_TRUE;
+			}
+		}
+		break;
+	case DCCPS_CLOSING:
+		(void) dccp_clean_death(dccp, 0);
+		return;
+	default:
+		if (connp->conn_debug) {
+			(void) strlog(DCCP_MOD_ID, 0, 1, SL_TRACE|SL_ERROR,
+			    "dccp_timer: strange state (%d) %s",
+			    dccp->dccp_state, dccp_display(dccp, NULL,
+			    DISP_PORT_ONLY));
+		}
+		return;
+	}
+
+	ASSERT(second_threshold != 0);
+
+	ms = 900;
+
+	DCCP_TIMER_RESTART(dccp, ms);
+
+	DCCPS_BUMP_MIB(dccps, dccpRetransSegs);
+}
