|
@@ -601,14 +601,14 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport,
|
|
|
|
|
|
/*
|
|
/*
|
|
* Transmit DATA chunks on the retransmit queue. Upon return from
|
|
* Transmit DATA chunks on the retransmit queue. Upon return from
|
|
- * sctp_outq_flush_rtx() the packet 'pkt' may contain chunks which
|
|
|
|
|
|
+ * __sctp_outq_flush_rtx() the packet 'pkt' may contain chunks which
|
|
* need to be transmitted by the caller.
|
|
* need to be transmitted by the caller.
|
|
* We assume that pkt->transport has already been set.
|
|
* We assume that pkt->transport has already been set.
|
|
*
|
|
*
|
|
* The return value is a normal kernel error return value.
|
|
* The return value is a normal kernel error return value.
|
|
*/
|
|
*/
|
|
-static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
|
|
|
|
- int rtx_timeout, int *start_timer)
|
|
|
|
|
|
+static int __sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
|
|
|
|
+ int rtx_timeout, int *start_timer)
|
|
{
|
|
{
|
|
struct sctp_transport *transport = pkt->transport;
|
|
struct sctp_transport *transport = pkt->transport;
|
|
struct sctp_chunk *chunk, *chunk1;
|
|
struct sctp_chunk *chunk, *chunk1;
|
|
@@ -987,6 +987,57 @@ static void sctp_outq_flush_ctrl(struct sctp_outq *q,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/* Returns false if new data shouldn't be sent */
|
|
|
|
+static bool sctp_outq_flush_rtx(struct sctp_outq *q,
|
|
|
|
+ struct sctp_transport **_transport,
|
|
|
|
+ struct list_head *transport_list,
|
|
|
|
+ int rtx_timeout)
|
|
|
|
+{
|
|
|
|
+ struct sctp_transport *transport = *_transport;
|
|
|
|
+ struct sctp_packet *packet = transport ? &transport->packet : NULL;
|
|
|
|
+ struct sctp_association *asoc = q->asoc;
|
|
|
|
+ int error, start_timer = 0;
|
|
|
|
+
|
|
|
|
+ if (asoc->peer.retran_path->state == SCTP_UNCONFIRMED)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ if (transport != asoc->peer.retran_path) {
|
|
|
|
+ /* Switch transports & prepare the packet. */
|
|
|
|
+ transport = asoc->peer.retran_path;
|
|
|
|
+ *_transport = transport;
|
|
|
|
+
|
|
|
|
+ if (list_empty(&transport->send_ready))
|
|
|
|
+ list_add_tail(&transport->send_ready,
|
|
|
|
+ transport_list);
|
|
|
|
+
|
|
|
|
+ packet = &transport->packet;
|
|
|
|
+ sctp_packet_config(packet, asoc->peer.i.init_tag,
|
|
|
|
+ asoc->peer.ecn_capable);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ error = __sctp_outq_flush_rtx(q, packet, rtx_timeout, &start_timer);
|
|
|
|
+ if (error < 0)
|
|
|
|
+ asoc->base.sk->sk_err = -error;
|
|
|
|
+
|
|
|
|
+ if (start_timer) {
|
|
|
|
+ sctp_transport_reset_t3_rtx(transport);
|
|
|
|
+ transport->last_time_sent = jiffies;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* This can happen on COOKIE-ECHO resend. Only
|
|
|
|
+ * one chunk can get bundled with a COOKIE-ECHO.
|
|
|
|
+ */
|
|
|
|
+ if (packet->has_cookie_echo)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ /* Don't send new data if there is still data
|
|
|
|
+ * waiting to retransmit.
|
|
|
|
+ */
|
|
|
|
+ if (!list_empty(&q->retransmit))
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ return true;
|
|
|
|
+}
|
|
/*
|
|
/*
|
|
* Try to flush an outqueue.
|
|
* Try to flush an outqueue.
|
|
*
|
|
*
|
|
@@ -1000,12 +1051,10 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
|
|
{
|
|
{
|
|
struct sctp_packet *packet;
|
|
struct sctp_packet *packet;
|
|
struct sctp_association *asoc = q->asoc;
|
|
struct sctp_association *asoc = q->asoc;
|
|
- __u32 vtag = asoc->peer.i.init_tag;
|
|
|
|
struct sctp_transport *transport = NULL;
|
|
struct sctp_transport *transport = NULL;
|
|
struct sctp_chunk *chunk;
|
|
struct sctp_chunk *chunk;
|
|
enum sctp_xmit status;
|
|
enum sctp_xmit status;
|
|
int error = 0;
|
|
int error = 0;
|
|
- int start_timer = 0;
|
|
|
|
|
|
|
|
/* These transports have chunks to send. */
|
|
/* These transports have chunks to send. */
|
|
struct list_head transport_list;
|
|
struct list_head transport_list;
|
|
@@ -1053,45 +1102,11 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
|
|
* current cwnd).
|
|
* current cwnd).
|
|
*/
|
|
*/
|
|
if (!list_empty(&q->retransmit)) {
|
|
if (!list_empty(&q->retransmit)) {
|
|
- if (asoc->peer.retran_path->state == SCTP_UNCONFIRMED)
|
|
|
|
- goto sctp_flush_out;
|
|
|
|
- if (transport == asoc->peer.retran_path)
|
|
|
|
- goto retran;
|
|
|
|
-
|
|
|
|
- /* Switch transports & prepare the packet. */
|
|
|
|
-
|
|
|
|
- transport = asoc->peer.retran_path;
|
|
|
|
-
|
|
|
|
- if (list_empty(&transport->send_ready)) {
|
|
|
|
- list_add_tail(&transport->send_ready,
|
|
|
|
- &transport_list);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
|
|
+ if (!sctp_outq_flush_rtx(q, &transport, &transport_list,
|
|
|
|
+ rtx_timeout))
|
|
|
|
+ break;
|
|
|
|
+ /* We may have switched current transport */
|
|
packet = &transport->packet;
|
|
packet = &transport->packet;
|
|
- sctp_packet_config(packet, vtag,
|
|
|
|
- asoc->peer.ecn_capable);
|
|
|
|
- retran:
|
|
|
|
- error = sctp_outq_flush_rtx(q, packet,
|
|
|
|
- rtx_timeout, &start_timer);
|
|
|
|
- if (error < 0)
|
|
|
|
- asoc->base.sk->sk_err = -error;
|
|
|
|
-
|
|
|
|
- if (start_timer) {
|
|
|
|
- sctp_transport_reset_t3_rtx(transport);
|
|
|
|
- transport->last_time_sent = jiffies;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* This can happen on COOKIE-ECHO resend. Only
|
|
|
|
- * one chunk can get bundled with a COOKIE-ECHO.
|
|
|
|
- */
|
|
|
|
- if (packet->has_cookie_echo)
|
|
|
|
- goto sctp_flush_out;
|
|
|
|
-
|
|
|
|
- /* Don't send new data if there is still data
|
|
|
|
- * waiting to retransmit.
|
|
|
|
- */
|
|
|
|
- if (!list_empty(&q->retransmit))
|
|
|
|
- goto sctp_flush_out;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/* Apply Max.Burst limitation to the current transport in
|
|
/* Apply Max.Burst limitation to the current transport in
|