diff options
author | Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> | 2018-05-14 13:34:37 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-05-14 22:57:14 -0400 |
commit | 0d634b0c94c9d8298f24a58e4af56c85dc06bb35 (patch) | |
tree | 9c929ba54bb692bfa50fc7fd704251c12e1eccac /net/sctp/outqueue.c | |
parent | b9fd683982c9d190cbccd8a32d885bf84bb4a12d (diff) |
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: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/sctp/outqueue.c')
-rw-r--r-- | net/sctp/outqueue.c | 187 |
1 files changed, 90 insertions, 97 deletions
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index 300bd0dfc7c1..bda50596d4bf 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c | |||
@@ -791,6 +791,90 @@ static int sctp_packet_singleton(struct sctp_transport *transport, | |||
791 | return sctp_packet_transmit(&singleton, gfp); | 791 | return sctp_packet_transmit(&singleton, gfp); |
792 | } | 792 | } |
793 | 793 | ||
794 | static bool sctp_outq_select_transport(struct sctp_chunk *chunk, | ||
795 | struct sctp_association *asoc, | ||
796 | struct sctp_transport **transport, | ||
797 | struct list_head *transport_list) | ||
798 | { | ||
799 | struct sctp_transport *new_transport = chunk->transport; | ||
800 | struct sctp_transport *curr = *transport; | ||
801 | bool changed = false; | ||
802 | |||
803 | if (!new_transport) { | ||
804 | if (!sctp_chunk_is_data(chunk)) { | ||
805 | /* | ||
806 | * If we have a prior transport pointer, see if | ||
807 | * the destination address of the chunk | ||
808 | * matches the destination address of the | ||
809 | * current transport. If not a match, then | ||
810 | * try to look up the transport with a given | ||
811 | * destination address. We do this because | ||
812 | * after processing ASCONFs, we may have new | ||
813 | * transports created. | ||
814 | */ | ||
815 | if (curr && sctp_cmp_addr_exact(&chunk->dest, | ||
816 | &curr->ipaddr)) | ||
817 | new_transport = curr; | ||
818 | else | ||
819 | new_transport = sctp_assoc_lookup_paddr(asoc, | ||
820 | &chunk->dest); | ||
821 | } | ||
822 | |||
823 | /* if we still don't have a new transport, then | ||
824 | * use the current active path. | ||
825 | */ | ||
826 | if (!new_transport) | ||
827 | new_transport = asoc->peer.active_path; | ||
828 | } else { | ||
829 | __u8 type; | ||
830 | |||
831 | switch (new_transport->state) { | ||
832 | case SCTP_INACTIVE: | ||
833 | case SCTP_UNCONFIRMED: | ||
834 | case SCTP_PF: | ||
835 | /* If the chunk is Heartbeat or Heartbeat Ack, | ||
836 | * send it to chunk->transport, even if it's | ||
837 | * inactive. | ||
838 | * | ||
839 | * 3.3.6 Heartbeat Acknowledgement: | ||
840 | * ... | ||
841 | * A HEARTBEAT ACK is always sent to the source IP | ||
842 | * address of the IP datagram containing the | ||
843 | * HEARTBEAT chunk to which this ack is responding. | ||
844 | * ... | ||
845 | * | ||
846 | * ASCONF_ACKs also must be sent to the source. | ||
847 | */ | ||
848 | type = chunk->chunk_hdr->type; | ||
849 | if (type != SCTP_CID_HEARTBEAT && | ||
850 | type != SCTP_CID_HEARTBEAT_ACK && | ||
851 | type != SCTP_CID_ASCONF_ACK) | ||
852 | new_transport = asoc->peer.active_path; | ||
853 | break; | ||
854 | default: | ||
855 | break; | ||
856 | } | ||
857 | } | ||
858 | |||
859 | /* Are we switching transports? Take care of transport locks. */ | ||
860 | if (new_transport != curr) { | ||
861 | changed = true; | ||
862 | curr = new_transport; | ||
863 | *transport = curr; | ||
864 | if (list_empty(&curr->send_ready)) | ||
865 | list_add_tail(&curr->send_ready, transport_list); | ||
866 | |||
867 | sctp_packet_config(&curr->packet, asoc->peer.i.init_tag, | ||
868 | asoc->peer.ecn_capable); | ||
869 | /* We've switched transports, so apply the | ||
870 | * Burst limit to the new transport. | ||
871 | */ | ||
872 | sctp_transport_burst_limited(curr); | ||
873 | } | ||
874 | |||
875 | return changed; | ||
876 | } | ||
877 | |||
794 | /* | 878 | /* |
795 | * Try to flush an outqueue. | 879 | * Try to flush an outqueue. |
796 | * | 880 | * |
@@ -806,7 +890,6 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp) | |||
806 | struct sctp_association *asoc = q->asoc; | 890 | struct sctp_association *asoc = q->asoc; |
807 | __u32 vtag = asoc->peer.i.init_tag; | 891 | __u32 vtag = asoc->peer.i.init_tag; |
808 | struct sctp_transport *transport = NULL; | 892 | struct sctp_transport *transport = NULL; |
809 | struct sctp_transport *new_transport; | ||
810 | struct sctp_chunk *chunk, *tmp; | 893 | struct sctp_chunk *chunk, *tmp; |
811 | enum sctp_xmit status; | 894 | enum sctp_xmit status; |
812 | int error = 0; | 895 | int error = 0; |
@@ -843,68 +926,12 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp) | |||
843 | 926 | ||
844 | list_del_init(&chunk->list); | 927 | list_del_init(&chunk->list); |
845 | 928 | ||
846 | /* Pick the right transport to use. */ | 929 | /* Pick the right transport to use. Should always be true for |
847 | new_transport = chunk->transport; | 930 | * the first chunk as we don't have a transport by then. |
848 | |||
849 | if (!new_transport) { | ||
850 | /* | ||
851 | * If we have a prior transport pointer, see if | ||
852 | * the destination address of the chunk | ||
853 | * matches the destination address of the | ||
854 | * current transport. If not a match, then | ||
855 | * try to look up the transport with a given | ||
856 | * destination address. We do this because | ||
857 | * after processing ASCONFs, we may have new | ||
858 | * transports created. | ||
859 | */ | ||
860 | if (transport && | ||
861 | sctp_cmp_addr_exact(&chunk->dest, | ||
862 | &transport->ipaddr)) | ||
863 | new_transport = transport; | ||
864 | else | ||
865 | new_transport = sctp_assoc_lookup_paddr(asoc, | ||
866 | &chunk->dest); | ||
867 | |||
868 | /* if we still don't have a new transport, then | ||
869 | * use the current active path. | ||
870 | */ | ||
871 | if (!new_transport) | ||
872 | new_transport = asoc->peer.active_path; | ||
873 | } else if ((new_transport->state == SCTP_INACTIVE) || | ||
874 | (new_transport->state == SCTP_UNCONFIRMED) || | ||
875 | (new_transport->state == SCTP_PF)) { | ||
876 | /* If the chunk is Heartbeat or Heartbeat Ack, | ||
877 | * send it to chunk->transport, even if it's | ||
878 | * inactive. | ||
879 | * | ||
880 | * 3.3.6 Heartbeat Acknowledgement: | ||
881 | * ... | ||
882 | * A HEARTBEAT ACK is always sent to the source IP | ||
883 | * address of the IP datagram containing the | ||
884 | * HEARTBEAT chunk to which this ack is responding. | ||
885 | * ... | ||
886 | * | ||
887 | * ASCONF_ACKs also must be sent to the source. | ||
888 | */ | ||
889 | if (chunk->chunk_hdr->type != SCTP_CID_HEARTBEAT && | ||
890 | chunk->chunk_hdr->type != SCTP_CID_HEARTBEAT_ACK && | ||
891 | chunk->chunk_hdr->type != SCTP_CID_ASCONF_ACK) | ||
892 | new_transport = asoc->peer.active_path; | ||
893 | } | ||
894 | |||
895 | /* Are we switching transports? | ||
896 | * Take care of transport locks. | ||
897 | */ | 931 | */ |
898 | if (new_transport != transport) { | 932 | if (sctp_outq_select_transport(chunk, asoc, &transport, |
899 | transport = new_transport; | 933 | &transport_list)) |
900 | if (list_empty(&transport->send_ready)) { | ||
901 | list_add_tail(&transport->send_ready, | ||
902 | &transport_list); | ||
903 | } | ||
904 | packet = &transport->packet; | 934 | packet = &transport->packet; |
905 | sctp_packet_config(packet, vtag, | ||
906 | asoc->peer.ecn_capable); | ||
907 | } | ||
908 | 935 | ||
909 | switch (chunk->chunk_hdr->type) { | 936 | switch (chunk->chunk_hdr->type) { |
910 | /* | 937 | /* |
@@ -1072,43 +1099,9 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp) | |||
1072 | goto sctp_flush_out; | 1099 | goto sctp_flush_out; |
1073 | } | 1100 | } |
1074 | 1101 | ||
1075 | /* If there is a specified transport, use it. | 1102 | if (sctp_outq_select_transport(chunk, asoc, &transport, |
1076 | * Otherwise, we want to use the active path. | 1103 | &transport_list)) |
1077 | */ | ||
1078 | new_transport = chunk->transport; | ||
1079 | if (!new_transport || | ||
1080 | ((new_transport->state == SCTP_INACTIVE) || | ||
1081 | (new_transport->state == SCTP_UNCONFIRMED) || | ||
1082 | (new_transport->state == SCTP_PF))) | ||
1083 | new_transport = asoc->peer.active_path; | ||
1084 | if (new_transport->state == SCTP_UNCONFIRMED) { | ||
1085 | WARN_ONCE(1, "Attempt to send packet on unconfirmed path."); | ||
1086 | sctp_sched_dequeue_done(q, chunk); | ||
1087 | sctp_chunk_fail(chunk, 0); | ||
1088 | sctp_chunk_free(chunk); | ||
1089 | continue; | ||
1090 | } | ||
1091 | |||
1092 | /* Change packets if necessary. */ | ||
1093 | if (new_transport != transport) { | ||
1094 | transport = new_transport; | ||
1095 | |||
1096 | /* Schedule to have this transport's | ||
1097 | * packet flushed. | ||
1098 | */ | ||
1099 | if (list_empty(&transport->send_ready)) { | ||
1100 | list_add_tail(&transport->send_ready, | ||
1101 | &transport_list); | ||
1102 | } | ||
1103 | |||
1104 | packet = &transport->packet; | 1104 | packet = &transport->packet; |
1105 | sctp_packet_config(packet, vtag, | ||
1106 | asoc->peer.ecn_capable); | ||
1107 | /* We've switched transports, so apply the | ||
1108 | * Burst limit to the new transport. | ||
1109 | */ | ||
1110 | sctp_transport_burst_limited(transport); | ||
1111 | } | ||
1112 | 1105 | ||
1113 | pr_debug("%s: outq:%p, chunk:%p[%s], tx-tsn:0x%x skb->head:%p " | 1106 | pr_debug("%s: outq:%p, chunk:%p[%s], tx-tsn:0x%x skb->head:%p " |
1114 | "skb->users:%d\n", | 1107 | "skb->users:%d\n", |