mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
sctp: add the probe timer in transport for PLPMTUD
There are 3 timers described in rfc8899#section-5.1.1: PROBE_TIMER, PMTU_RAISE_TIMER, CONFIRMATION_TIMER This patches adds a 'probe_timer' in transport, and it works as either PROBE_TIMER or PMTU_RAISE_TIMER. At most time, it works as PROBE_TIMER and expires every a 'probe_interval' time to send the HB probe packet. When transport pl enters COMPLETE state, it works as PMTU_RAISE_TIMER and expires in 'probe_interval * 30' time to go back to SEARCH state and do searching again. SCTP HB is an acknowledged packet, CONFIRMATION_TIMER is not needed. The timer will start when transport pl enters BASE state and stop when it enters DISABLED state. Signed-off-by: Xin Long <lucien.xin@gmail.com> Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
d9e2e410ae
commit
92548ec2f1
@ -59,6 +59,7 @@ enum sctp_verb {
|
||||
SCTP_CMD_HB_TIMERS_START, /* Start the heartbeat timers. */
|
||||
SCTP_CMD_HB_TIMER_UPDATE, /* Update a heartbeat timers. */
|
||||
SCTP_CMD_HB_TIMERS_STOP, /* Stop the heartbeat timers. */
|
||||
SCTP_CMD_PROBE_TIMER_UPDATE, /* Update a probe timer. */
|
||||
SCTP_CMD_TRANSPORT_HB_SENT, /* Reset the status of a transport. */
|
||||
SCTP_CMD_TRANSPORT_IDLE, /* Do manipulations on idle transport */
|
||||
SCTP_CMD_TRANSPORT_ON, /* Mark the transport as active. */
|
||||
|
@ -77,6 +77,7 @@ enum sctp_event_timeout {
|
||||
SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD,
|
||||
SCTP_EVENT_TIMEOUT_HEARTBEAT,
|
||||
SCTP_EVENT_TIMEOUT_RECONF,
|
||||
SCTP_EVENT_TIMEOUT_PROBE,
|
||||
SCTP_EVENT_TIMEOUT_SACK,
|
||||
SCTP_EVENT_TIMEOUT_AUTOCLOSE,
|
||||
};
|
||||
|
@ -635,10 +635,14 @@ static inline void sctp_transport_pl_reset(struct sctp_transport *t)
|
||||
t->pl.state = SCTP_PL_BASE;
|
||||
t->pl.pmtu = SCTP_BASE_PLPMTU;
|
||||
t->pl.probe_size = SCTP_BASE_PLPMTU;
|
||||
sctp_transport_reset_probe_timer(t);
|
||||
}
|
||||
} else {
|
||||
if (t->pl.state != SCTP_PL_DISABLED)
|
||||
if (t->pl.state != SCTP_PL_DISABLED) {
|
||||
if (del_timer(&t->probe_timer))
|
||||
sctp_transport_put(t);
|
||||
t->pl.state = SCTP_PL_DISABLED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -647,6 +651,9 @@ static inline void sctp_transport_pl_update(struct sctp_transport *t)
|
||||
if (t->pl.state == SCTP_PL_DISABLED)
|
||||
return;
|
||||
|
||||
if (del_timer(&t->probe_timer))
|
||||
sctp_transport_put(t);
|
||||
|
||||
t->pl.state = SCTP_PL_BASE;
|
||||
t->pl.pmtu = SCTP_BASE_PLPMTU;
|
||||
t->pl.probe_size = SCTP_BASE_PLPMTU;
|
||||
|
@ -151,6 +151,7 @@ sctp_state_fn_t sctp_sf_cookie_wait_icmp_abort;
|
||||
/* Prototypes for timeout event state functions. */
|
||||
sctp_state_fn_t sctp_sf_do_6_3_3_rtx;
|
||||
sctp_state_fn_t sctp_sf_send_reconf;
|
||||
sctp_state_fn_t sctp_sf_send_probe;
|
||||
sctp_state_fn_t sctp_sf_do_6_2_sack;
|
||||
sctp_state_fn_t sctp_sf_autoclose_timer_expire;
|
||||
|
||||
@ -311,6 +312,7 @@ int sctp_do_sm(struct net *net, enum sctp_event_type event_type,
|
||||
void sctp_generate_t3_rtx_event(struct timer_list *t);
|
||||
void sctp_generate_heartbeat_event(struct timer_list *t);
|
||||
void sctp_generate_reconf_event(struct timer_list *t);
|
||||
void sctp_generate_probe_event(struct timer_list *t);
|
||||
void sctp_generate_proto_unreach_event(struct timer_list *t);
|
||||
|
||||
void sctp_ootb_pkt_free(struct sctp_packet *packet);
|
||||
|
@ -936,6 +936,9 @@ struct sctp_transport {
|
||||
/* Timer to handler reconf chunk rtx */
|
||||
struct timer_list reconf_timer;
|
||||
|
||||
/* Timer to send a probe HB packet for PLPMTUD */
|
||||
struct timer_list probe_timer;
|
||||
|
||||
/* Since we're using per-destination retransmission timers
|
||||
* (see above), we're also using per-destination "transmitted"
|
||||
* queues. This probably ought to be a private struct
|
||||
@ -1003,6 +1006,7 @@ void sctp_transport_free(struct sctp_transport *);
|
||||
void sctp_transport_reset_t3_rtx(struct sctp_transport *);
|
||||
void sctp_transport_reset_hb_timer(struct sctp_transport *);
|
||||
void sctp_transport_reset_reconf_timer(struct sctp_transport *transport);
|
||||
void sctp_transport_reset_probe_timer(struct sctp_transport *transport);
|
||||
int sctp_transport_hold(struct sctp_transport *);
|
||||
void sctp_transport_put(struct sctp_transport *);
|
||||
void sctp_transport_update_rto(struct sctp_transport *, __u32);
|
||||
|
@ -154,6 +154,7 @@ static const char *const sctp_timer_tbl[] = {
|
||||
"TIMEOUT_T5_SHUTDOWN_GUARD",
|
||||
"TIMEOUT_HEARTBEAT",
|
||||
"TIMEOUT_RECONF",
|
||||
"TIMEOUT_PROBE",
|
||||
"TIMEOUT_SACK",
|
||||
"TIMEOUT_AUTOCLOSE",
|
||||
};
|
||||
|
@ -471,6 +471,38 @@ out_unlock:
|
||||
sctp_transport_put(transport);
|
||||
}
|
||||
|
||||
/* Handle the timeout of the probe timer. */
|
||||
void sctp_generate_probe_event(struct timer_list *t)
|
||||
{
|
||||
struct sctp_transport *transport = from_timer(transport, t, probe_timer);
|
||||
struct sctp_association *asoc = transport->asoc;
|
||||
struct sock *sk = asoc->base.sk;
|
||||
struct net *net = sock_net(sk);
|
||||
int error = 0;
|
||||
|
||||
bh_lock_sock(sk);
|
||||
if (sock_owned_by_user(sk)) {
|
||||
pr_debug("%s: sock is busy\n", __func__);
|
||||
|
||||
/* Try again later. */
|
||||
if (!mod_timer(&transport->probe_timer, jiffies + (HZ / 20)))
|
||||
sctp_transport_hold(transport);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
error = sctp_do_sm(net, SCTP_EVENT_T_TIMEOUT,
|
||||
SCTP_ST_TIMEOUT(SCTP_EVENT_TIMEOUT_PROBE),
|
||||
asoc->state, asoc->ep, asoc,
|
||||
transport, GFP_ATOMIC);
|
||||
|
||||
if (error)
|
||||
sk->sk_err = -error;
|
||||
|
||||
out_unlock:
|
||||
bh_unlock_sock(sk);
|
||||
sctp_transport_put(transport);
|
||||
}
|
||||
|
||||
/* Inject a SACK Timeout event into the state machine. */
|
||||
static void sctp_generate_sack_event(struct timer_list *t)
|
||||
{
|
||||
@ -1641,6 +1673,11 @@ static int sctp_cmd_interpreter(enum sctp_event_type event_type,
|
||||
sctp_cmd_hb_timers_stop(commands, asoc);
|
||||
break;
|
||||
|
||||
case SCTP_CMD_PROBE_TIMER_UPDATE:
|
||||
t = cmd->obj.transport;
|
||||
sctp_transport_reset_probe_timer(t);
|
||||
break;
|
||||
|
||||
case SCTP_CMD_REPORT_ERROR:
|
||||
error = cmd->obj.error;
|
||||
break;
|
||||
|
@ -1095,6 +1095,23 @@ enum sctp_disposition sctp_sf_send_reconf(struct net *net,
|
||||
return SCTP_DISPOSITION_CONSUME;
|
||||
}
|
||||
|
||||
/* send hb chunk with padding for PLPMUTD. */
|
||||
enum sctp_disposition sctp_sf_send_probe(struct net *net,
|
||||
const struct sctp_endpoint *ep,
|
||||
const struct sctp_association *asoc,
|
||||
const union sctp_subtype type,
|
||||
void *arg,
|
||||
struct sctp_cmd_seq *commands)
|
||||
{
|
||||
struct sctp_transport *transport = (struct sctp_transport *)arg;
|
||||
|
||||
/* The actual handling will be performed here in a later patch. */
|
||||
sctp_add_cmd_sf(commands, SCTP_CMD_PROBE_TIMER_UPDATE,
|
||||
SCTP_TRANSPORT(transport));
|
||||
|
||||
return SCTP_DISPOSITION_CONSUME;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process an heartbeat request.
|
||||
*
|
||||
|
@ -967,6 +967,25 @@ other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STATES] = {
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
}
|
||||
|
||||
#define TYPE_SCTP_EVENT_TIMEOUT_PROBE { \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_send_probe), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
}
|
||||
|
||||
static const struct sctp_sm_table_entry
|
||||
timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES] = {
|
||||
TYPE_SCTP_EVENT_TIMEOUT_NONE,
|
||||
@ -978,6 +997,7 @@ timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES] = {
|
||||
TYPE_SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD,
|
||||
TYPE_SCTP_EVENT_TIMEOUT_HEARTBEAT,
|
||||
TYPE_SCTP_EVENT_TIMEOUT_RECONF,
|
||||
TYPE_SCTP_EVENT_TIMEOUT_PROBE,
|
||||
TYPE_SCTP_EVENT_TIMEOUT_SACK,
|
||||
TYPE_SCTP_EVENT_TIMEOUT_AUTOCLOSE,
|
||||
};
|
||||
|
@ -75,6 +75,7 @@ static struct sctp_transport *sctp_transport_init(struct net *net,
|
||||
timer_setup(&peer->T3_rtx_timer, sctp_generate_t3_rtx_event, 0);
|
||||
timer_setup(&peer->hb_timer, sctp_generate_heartbeat_event, 0);
|
||||
timer_setup(&peer->reconf_timer, sctp_generate_reconf_event, 0);
|
||||
timer_setup(&peer->probe_timer, sctp_generate_probe_event, 0);
|
||||
timer_setup(&peer->proto_unreach_timer,
|
||||
sctp_generate_proto_unreach_event, 0);
|
||||
|
||||
@ -131,6 +132,9 @@ void sctp_transport_free(struct sctp_transport *transport)
|
||||
if (del_timer(&transport->reconf_timer))
|
||||
sctp_transport_put(transport);
|
||||
|
||||
if (del_timer(&transport->probe_timer))
|
||||
sctp_transport_put(transport);
|
||||
|
||||
/* Delete the ICMP proto unreachable timer if it's active. */
|
||||
if (del_timer(&transport->proto_unreach_timer))
|
||||
sctp_transport_put(transport);
|
||||
@ -207,6 +211,20 @@ void sctp_transport_reset_reconf_timer(struct sctp_transport *transport)
|
||||
sctp_transport_hold(transport);
|
||||
}
|
||||
|
||||
void sctp_transport_reset_probe_timer(struct sctp_transport *transport)
|
||||
{
|
||||
int scale = 1;
|
||||
|
||||
if (timer_pending(&transport->probe_timer))
|
||||
return;
|
||||
if (transport->pl.state == SCTP_PL_COMPLETE &&
|
||||
transport->pl.probe_count == 1)
|
||||
scale = 30; /* works as PMTU_RAISE_TIMER */
|
||||
if (!mod_timer(&transport->probe_timer,
|
||||
jiffies + transport->probe_interval * scale))
|
||||
sctp_transport_hold(transport);
|
||||
}
|
||||
|
||||
/* This transport has been assigned to an association.
|
||||
* Initialize fields from the association or from the sock itself.
|
||||
* Register the reference count in the association.
|
||||
|
Loading…
Reference in New Issue
Block a user