summaryrefslogtreecommitdiff
path: root/usr/src/uts/common
diff options
context:
space:
mode:
authorBryan Cantrill <bryan@joyent.com>2011-11-01 13:56:52 +0000
committerBryan Cantrill <bryan@joyent.com>2011-11-01 13:56:52 +0000
commit4047c26b19013d5df8a1091520616a805829194c (patch)
tree02e4978e48825adc2647abcb1c62b38ca952e11f /usr/src/uts/common
parentac17a7b157f7bdfbe781fe494f306d70a84915ca (diff)
downloadillumos-joyent-4047c26b19013d5df8a1091520616a805829194c.tar.gz
OS-691 panic in conn_ip_output
Diffstat (limited to 'usr/src/uts/common')
-rw-r--r--usr/src/uts/common/inet/tcp/tcp_timers.c32
1 files changed, 22 insertions, 10 deletions
diff --git a/usr/src/uts/common/inet/tcp/tcp_timers.c b/usr/src/uts/common/inet/tcp/tcp_timers.c
index 90e1c9178c..a18c7dd8b1 100644
--- a/usr/src/uts/common/inet/tcp/tcp_timers.c
+++ b/usr/src/uts/common/inet/tcp/tcp_timers.c
@@ -72,10 +72,8 @@
* request. locks acquired by the call-back routine should not be held across
* the call to tcp_timeout_cancel() or a deadlock may result.
*
- * tcp_timeout_cancel() returns -1 if it can not cancel the timeout request.
- * Otherwise, it returns an integer value greater than or equal to 0. In
- * particular, if the call-back function is already placed on the squeue, it can
- * not be canceled.
+ * tcp_timeout_cancel() returns -1 if the timeout request is invalid.
+ * Otherwise, it returns an integer value greater than or equal to 0.
*
* NOTE: both tcp_timeout() and tcp_timeout_cancel() should always be called
* within squeue context corresponding to the tcp instance. Since the
@@ -89,12 +87,6 @@
* and stores the return value of tcp_timeout() in the tcp->tcp_timer_tid
* field.
*
- * NOTE: since the timeout cancellation is not guaranteed, the cancelled
- * call-back may still be called, so it is possible tcp_timer() will be
- * called several times. This should not be a problem since tcp_timer()
- * should always check the tcp instance state.
- *
- *
* IMPLEMENTATION:
*
* TCP timers are implemented using three-stage process. The call to
@@ -173,6 +165,7 @@ tcp_timeout(conn_t *connp, void (*f)(void *), hrtime_t tim)
*/
tcpt->tcpt_tid = timeout_generic(CALLOUT_NORMAL, tcp_timer_callback, mp,
tim * MICROSEC, CALLOUT_TCP_RESOLUTION, CALLOUT_FLAG_ROUNDUP);
+ VERIFY(!(tcpt->tcpt_tid & CALLOUT_ID_FREE));
return ((timeout_id_t)mp);
}
@@ -202,6 +195,15 @@ tcp_timer_handler(void *arg, mblk_t *mp, void *arg2, ip_recv_attr_t *dummy)
ASSERT(connp == tcpt->connp);
ASSERT((squeue_t *)arg2 == connp->conn_sqp);
+ if (tcpt->tcpt_tid & CALLOUT_ID_FREE) {
+ /*
+ * This timeout was cancelled after it was enqueued to the
+ * squeue; free the timer and return.
+ */
+ tcp_timer_free(connp->conn_tcp, mp);
+ return;
+ }
+
/*
* If the TCP has reached the closed state, don't proceed any
* further. This TCP logically does not exist on the system.
@@ -213,6 +215,7 @@ tcp_timer_handler(void *arg, mblk_t *mp, void *arg2, ip_recv_attr_t *dummy)
} else {
tcp->tcp_timer_tid = 0;
}
+
tcp_timer_free(connp->conn_tcp, mp);
}
@@ -243,6 +246,15 @@ tcp_timeout_cancel(conn_t *connp, timeout_id_t id)
TCP_DBGSTAT(connp->conn_tcp->tcp_tcps, tcp_timeout_canceled);
tcp_timer_free(connp->conn_tcp, mp);
CONN_DEC_REF(connp);
+ } else {
+ /*
+ * If we were unable to untimeout successfully, it has already
+ * been enqueued on the squeue; mark the ID with the free
+ * bit. This bit can never be set in a valid identifier, and
+ * we'll use it to prevent the timeout from being executed.
+ */
+ tcpt->tcpt_tid |= CALLOUT_ID_FREE;
+ delta = 0;
}
return (TICK_TO_MSEC(delta));