Commit 0d634b0c authored by Marcelo Ricardo Leitner's avatar Marcelo Ricardo Leitner Committed by David S. Miller

sctp: factor out sctp_outq_select_transport

We had two spots doing such complex operation and they were very close to
each other, a bit more tailored to here or there.

This patch unifies these under the same function,
sctp_outq_select_transport, which knows how to handle control chunks and
original transmissions (but not retransmissions).
Signed-off-by: default avatarMarcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b9fd6839
...@@ -791,6 +791,90 @@ static int sctp_packet_singleton(struct sctp_transport *transport, ...@@ -791,6 +791,90 @@ static int sctp_packet_singleton(struct sctp_transport *transport,
return sctp_packet_transmit(&singleton, gfp); return sctp_packet_transmit(&singleton, gfp);
} }
static bool sctp_outq_select_transport(struct sctp_chunk *chunk,
struct sctp_association *asoc,
struct sctp_transport **transport,
struct list_head *transport_list)
{
struct sctp_transport *new_transport = chunk->transport;
struct sctp_transport *curr = *transport;
bool changed = false;
if (!new_transport) {
if (!sctp_chunk_is_data(chunk)) {
/*
* If we have a prior transport pointer, see if
* the destination address of the chunk
* matches the destination address of the
* current transport. If not a match, then
* try to look up the transport with a given
* destination address. We do this because
* after processing ASCONFs, we may have new
* transports created.
*/
if (curr && sctp_cmp_addr_exact(&chunk->dest,
&curr->ipaddr))
new_transport = curr;
else
new_transport = sctp_assoc_lookup_paddr(asoc,
&chunk->dest);
}
/* if we still don't have a new transport, then
* use the current active path.
*/
if (!new_transport)
new_transport = asoc->peer.active_path;
} else {
__u8 type;
switch (new_transport->state) {
case SCTP_INACTIVE:
case SCTP_UNCONFIRMED:
case SCTP_PF:
/* If the chunk is Heartbeat or Heartbeat Ack,
* send it to chunk->transport, even if it's
* inactive.
*
* 3.3.6 Heartbeat Acknowledgement:
* ...
* A HEARTBEAT ACK is always sent to the source IP
* address of the IP datagram containing the
* HEARTBEAT chunk to which this ack is responding.
* ...
*
* ASCONF_ACKs also must be sent to the source.
*/
type = chunk->chunk_hdr->type;
if (type != SCTP_CID_HEARTBEAT &&
type != SCTP_CID_HEARTBEAT_ACK &&
type != SCTP_CID_ASCONF_ACK)
new_transport = asoc->peer.active_path;
break;
default:
break;
}
}
/* Are we switching transports? Take care of transport locks. */
if (new_transport != curr) {
changed = true;
curr = new_transport;
*transport = curr;
if (list_empty(&curr->send_ready))
list_add_tail(&curr->send_ready, transport_list);
sctp_packet_config(&curr->packet, asoc->peer.i.init_tag,
asoc->peer.ecn_capable);
/* We've switched transports, so apply the
* Burst limit to the new transport.
*/
sctp_transport_burst_limited(curr);
}
return changed;
}
/* /*
* Try to flush an outqueue. * Try to flush an outqueue.
* *
...@@ -806,7 +890,6 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp) ...@@ -806,7 +890,6 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
struct sctp_association *asoc = q->asoc; struct sctp_association *asoc = q->asoc;
__u32 vtag = asoc->peer.i.init_tag; __u32 vtag = asoc->peer.i.init_tag;
struct sctp_transport *transport = NULL; struct sctp_transport *transport = NULL;
struct sctp_transport *new_transport;
struct sctp_chunk *chunk, *tmp; struct sctp_chunk *chunk, *tmp;
enum sctp_xmit status; enum sctp_xmit status;
int error = 0; int error = 0;
...@@ -843,68 +926,12 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp) ...@@ -843,68 +926,12 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
list_del_init(&chunk->list); list_del_init(&chunk->list);
/* Pick the right transport to use. */ /* Pick the right transport to use. Should always be true for
new_transport = chunk->transport; * the first chunk as we don't have a transport by then.
if (!new_transport) {
/*
* If we have a prior transport pointer, see if
* the destination address of the chunk
* matches the destination address of the
* current transport. If not a match, then
* try to look up the transport with a given
* destination address. We do this because
* after processing ASCONFs, we may have new
* transports created.
*/
if (transport &&
sctp_cmp_addr_exact(&chunk->dest,
&transport->ipaddr))
new_transport = transport;
else
new_transport = sctp_assoc_lookup_paddr(asoc,
&chunk->dest);
/* if we still don't have a new transport, then
* use the current active path.
*/
if (!new_transport)
new_transport = asoc->peer.active_path;
} else if ((new_transport->state == SCTP_INACTIVE) ||
(new_transport->state == SCTP_UNCONFIRMED) ||
(new_transport->state == SCTP_PF)) {
/* If the chunk is Heartbeat or Heartbeat Ack,
* send it to chunk->transport, even if it's
* inactive.
*
* 3.3.6 Heartbeat Acknowledgement:
* ...
* A HEARTBEAT ACK is always sent to the source IP
* address of the IP datagram containing the
* HEARTBEAT chunk to which this ack is responding.
* ...
*
* ASCONF_ACKs also must be sent to the source.
*/
if (chunk->chunk_hdr->type != SCTP_CID_HEARTBEAT &&
chunk->chunk_hdr->type != SCTP_CID_HEARTBEAT_ACK &&
chunk->chunk_hdr->type != SCTP_CID_ASCONF_ACK)
new_transport = asoc->peer.active_path;
}
/* Are we switching transports?
* Take care of transport locks.
*/ */
if (new_transport != transport) { if (sctp_outq_select_transport(chunk, asoc, &transport,
transport = new_transport; &transport_list))
if (list_empty(&transport->send_ready)) {
list_add_tail(&transport->send_ready,
&transport_list);
}
packet = &transport->packet; packet = &transport->packet;
sctp_packet_config(packet, vtag,
asoc->peer.ecn_capable);
}
switch (chunk->chunk_hdr->type) { switch (chunk->chunk_hdr->type) {
/* /*
...@@ -1072,43 +1099,9 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp) ...@@ -1072,43 +1099,9 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
goto sctp_flush_out; goto sctp_flush_out;
} }
/* If there is a specified transport, use it. if (sctp_outq_select_transport(chunk, asoc, &transport,
* Otherwise, we want to use the active path. &transport_list))
*/
new_transport = chunk->transport;
if (!new_transport ||
((new_transport->state == SCTP_INACTIVE) ||
(new_transport->state == SCTP_UNCONFIRMED) ||
(new_transport->state == SCTP_PF)))
new_transport = asoc->peer.active_path;
if (new_transport->state == SCTP_UNCONFIRMED) {
WARN_ONCE(1, "Attempt to send packet on unconfirmed path.");
sctp_sched_dequeue_done(q, chunk);
sctp_chunk_fail(chunk, 0);
sctp_chunk_free(chunk);
continue;
}
/* Change packets if necessary. */
if (new_transport != transport) {
transport = new_transport;
/* Schedule to have this transport's
* packet flushed.
*/
if (list_empty(&transport->send_ready)) {
list_add_tail(&transport->send_ready,
&transport_list);
}
packet = &transport->packet; packet = &transport->packet;
sctp_packet_config(packet, vtag,
asoc->peer.ecn_capable);
/* We've switched transports, so apply the
* Burst limit to the new transport.
*/
sctp_transport_burst_limited(transport);
}
pr_debug("%s: outq:%p, chunk:%p[%s], tx-tsn:0x%x skb->head:%p " pr_debug("%s: outq:%p, chunk:%p[%s], tx-tsn:0x%x skb->head:%p "
"skb->users:%d\n", "skb->users:%d\n",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment