diff options
author | Bryan Cantrill <bryan@joyent.com> | 2011-11-01 13:56:52 +0000 |
---|---|---|
committer | Bryan Cantrill <bryan@joyent.com> | 2011-11-01 13:56:52 +0000 |
commit | 4047c26b19013d5df8a1091520616a805829194c (patch) | |
tree | 02e4978e48825adc2647abcb1c62b38ca952e11f /usr/src/uts/common | |
parent | ac17a7b157f7bdfbe781fe494f306d70a84915ca (diff) | |
download | illumos-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.c | 32 |
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)); |