/*
* 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
* 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
*/
/*
*/
#include <inet/ipclassifier.h>
#include "sctp_impl.h"
#include "sctp_asconf.h"
/* Timer block states. */
typedef enum {
typedef struct sctp_tb_s {
} sctp_tb_t;
/*
* Early abort threshold when the system is under pressure, sctps_reclaim
* is on.
*
* sctp_pa_early_abort: number of strikes per association before abort
* sctp_pp_early_abort: number of strikes per peer address before abort
*/
static void sctp_timer_fire(sctp_tb_t *);
/*
* sctp_timer mechanism.
*
* Each timer is represented by a timer mblk. When the
* timer fires, and the sctp_t is busy, the timer mblk will be put on
* the associated sctp_t timer queue so that it can be executed when
* the thread holding the lock on the sctp_t is done with its job.
*
* Note that there is no lock to protect the timer mblk state. The reason
* is that the timer state can only be changed by a thread holding the
* lock on the sctp_t.
*
* The interface consists of 4 entry points:
* sctp_timer_alloc - create a timer mblk
* sctp_timer_free - free a timer mblk
* sctp_timer - start, restart, stop the timer
* sctp_timer_valid - called by sctp_process_recvq to verify that
* the timer did indeed fire.
*/
/*
* Start, restart, stop the timer.
* If "tim" is -1 the timer is stopped.
* Otherwise, the timer is stopped if it is already running, and
* set to fire tim clock ticks from now.
*/
void
{
int state;
if (tim >= 0) {
if (state == SCTP_TB_RUNNING) {
/* sctp_timer_valid will start timer */
return;
}
} else if (state != SCTP_TB_IDLE) {
if (state == SCTP_TB_CANCELLED) {
/* sctp_timer_valid will start timer */
return;
}
if (state == SCTP_TB_RESCHED) {
/* sctp_timer_valid will start timer */
return;
}
} else {
}
return;
}
switch (tim) {
case -1:
break;
default:
ASSERT(0);
break;
}
}
/*
* sctp_timer_alloc is called by sctp_init to allocate and initialize a
* sctp timer.
*
* Allocate an M_PCSIG timer message. The space between db_base and
* b_rptr is used by the sctp_timer mechanism, and after b_rptr there is
* space for sctpt_t.
*/
mblk_t *
{
} else {
}
return (mp);
}
return (NULL);
}
/*
* timeout() callback function.
* Put the message on the process control block's queue.
* If the timer is stopped or freed after
* it has fired then sctp_timer() and sctp_timer_valid() will clean
* things up.
*/
static void
{
if (sctp->sctp_running) {
/*
* Put the timer mblk to the special sctp_timer_mp list.
* This timer will be handled when the thread using this
* SCTP is done with its job.
*/
} else {
}
} else {
}
}
/*
* Logically free a timer mblk (that might have a pending timeout().)
* If the timer has fired and the mblk has been put on the queue then
* sctp_timer_valid will free the mblk.
*/
void
{
int state;
if (state == SCTP_TB_RUNNING) {
/* sctp_timer_valid will free the mblk */
return;
}
} else if (state != SCTP_TB_IDLE) {
/* sctp_timer_valid will free the mblk */
return;
}
}
/*
* Called from sctp_timer(,,-1)
*/
void
{
int state;
if (state == SCTP_TB_RUNNING) {
} else {
}
} else if (state == SCTP_TB_RESCHED) {
}
}
/*
* The user of the sctp_timer mechanism is required to call
* sctp_timer_valid() for each M_PCSIG message processed in the
* service procedures.
* sctp_timer_valid will return "true" if the timer actually did fire.
*/
static boolean_t
{
int state;
if (state != SCTP_TB_RUNNING) {
if (state == SCTP_TB_TO_BE_FREED) {
/*
* sctp_timer_free was called after the message
* was putq'ed.
*/
return (B_FALSE);
}
if (state == SCTP_TB_CANCELLED) {
/* The timer was stopped after the mblk was putq'ed */
return (B_FALSE);
}
if (state == SCTP_TB_RESCHED) {
/*
* The timer was stopped and then restarted after
* the mblk was putq'ed.
* sctp_tb_time_left contains the number of ticks that
* the timer was restarted with.
* The sctp will not be disapper between the time
* the sctpt_t is marked SCTP_TB_RESCHED and when
* we get here as sctp_add_recvq() does a refhold.
*/
return (B_FALSE);
}
}
return (B_TRUE);
}
/*
* The SCTP timer call. Calls sctp_timer_valid() to verify whether
* timer was cancelled or not.
*/
void
{
if (sctp_timer_valid(mp)) {
}
}
/*
* Delayed ack
*/
void
{
sctp->sctp_ack_timer_running = 0;
}
/*
* Peer address heartbeat timer handler
*/
void
{
int cnt;
int pp_max_retr;
/*
* If there is a peer address with no strikes, don't give up
* yet unless we are under memory pressure. If enough other
* peer address are down, we could otherwise fail the
* association prematurely. This is a byproduct of our
* aggressive probe approach when a heartbeat fails to
* connect. We may wish to revisit this...
*/
/* time to give up */
return;
}
}
/* Only send heartbeats in the established state */
return;
}
now = ddi_get_lbolt64();
earliest_expiry = 0;
/*
* Walk through all faddrs. Since the timer should run infrequently
* and the number of peer addresses should not be big, this should
* be OK.
*/
if (sctps->sctps_reclaim)
else
/*
* If the peer is unreachable because there is no available
* source address, call sctp_get_dest() to see if it is
* reachable now. If it is OK, the state will become
* unconfirmed. And the following code to handle unconfirmed
* address will be executed. If it is still not OK,
* re-schedule. If heartbeat is enabled, only try this
* up to the normal heartbeat max times. But if heartbeat
* is disable, this retry may go on forever.
*/
if (fp->sf_hb_enabled &&
SCTP_FADDRS_DOWN) == -1) {
/* Assoc is dead */
return;
}
goto set_expiry;
} else {
/* Send a heartbeat immediately. */
}
}
/*
* Don't send heartbeat to this address if it is not
* hb_enabled and the address has been confirmed.
*/
continue;
}
/*
* The heartbeat timer is expired. If the address is dead,
* we still send heartbeat to it in case it becomes alive
* again. But we will only send once in a while, calculated
* by SET_HB_INTVL().
*
* If the address is alive and there is a hearbeat pending,
* resend the heartbeat and start exponential backoff on the
* heartbeat timeout value. If there is no heartbeat pending,
* just send out one.
*/
if (fp->sf_hb_pending) {
/*
* If an address is not confirmed, no need
* to bump the overall counter as it doesn't
* matter as we will not use it to send data
* and it should not affect the association.
*/
case SCTP_FADDRS_ALIVE:
sctp->sctp_strikes++;
/* FALLTHRU */
case SCTP_FADDRS_UNCONFIRMED:
/*
* Retransmission implies that RTO
* is probably not correct.
*/
fp->sf_rtt_updates = 0;
fp->sf_strikes++;
SCTP_FADDRS_DOWN) == -1) {
/* Assoc is dead */
return;
}
/*
* Addr is down; keep initial
* RTO
*/
goto dead_addr;
} else {
sctp->sctp_rto_max);
}
break;
case SCTP_FADDRS_DOWN:
break;
default:
continue;
}
} else {
/*
* If there is unack'ed data, no need to
* send a heart beat.
*/
goto set_expiry;
} else {
}
}
/*
* Note that the total number of heartbeat we can send
* out simultaneously is limited by sctp_maxburst. If
* the limit is exceeded, we need to wait for the next
* timeout to send them. This should only happen if
* there is unconfirmed address. Note that hb_pending
* is set in sctp_send_heartbeat(). So if a heartbeat
* is not sent, it will not affect the state of the
* peer address.
*/
cnt-- > 0)
}
}
if (sctp->sctp_autoclose != 0) {
sctp_send_shutdown(sctp, 0);
return;
}
}
earliest_expiry -= now;
if (earliest_expiry < 0)
earliest_expiry = 1;
}
void
{
fp->sf_timer_running = 0;
if (!sctps->sctps_reclaim) {
} else {
/* App may have set a very aggressive retransmission limit. */
}
/* Check is we've reached the max for retries */
/* time to give up */
return;
}
/* time to give up */
return;
}
}
return;
}
}
switch (sctp->sctp_state) {
case SCTPS_SHUTDOWN_RECEIVED:
NULL);
/* FALLTHRU */
case SCTPS_ESTABLISHED:
case SCTPS_SHUTDOWN_PENDING:
/* Nothing to retransmit */
}
return;
}
/*
* sctp_rexmit() will increase the strikes and restart the
* timer, so return here.
*/
return;
case SCTPS_COOKIE_WAIT:
/* retransmit init */
/*
* We don't take the conn hash lock here since the source
* address list won't be modified (it would have been done
* the first time around).
*/
}
break;
case SCTPS_COOKIE_ECHOED:
goto rxmit_init;
}
break;
break;
case SCTPS_SHUTDOWN_SENT:
break;
case SCTPS_SHUTDOWN_ACK_SENT:
/* We shouldn't have any more outstanding data */
NULL);
break;
default:
ASSERT(0);
break;
}
fp->sf_strikes++;
sctp->sctp_strikes++;
}
/*
* RTO calculation. timesent and now are both in ms.
*/
void
{
int rtt;
/* Calculate the RTT in ms */
/* Is this the first RTT measurement? */
} else {
int abs;
/*
* Versions of the RTO equations that use fixed-point math.
* alpha and beta are NOT tunable in this implementation,
* and so are hard-coded in. alpha = 1/8, beta = 1/4.
*/
}
/* Bound the RTO by configured min and max values */
}
}
fp->sf_rtt_updates++;
}
void
{
fp->sf_timer_running = 0;
}
fp->sf_rc_timer_running = 0;
}
}
}
void
{
}
}
void
{
/*
* Since the timer mblk can be freed in sctp_timer_call(),
* we need to grab the b_cont before that.
*/
/*
* We have a reference on the sctp, the lock must be
* dropped to avoid deadlocks with functions potentially
* called in this context which in turn call untimeout().
*/
}
}