Skip to content

Commit d003b41

Browse files
Lee A. Robertsdavem330
authored andcommitted
sctp: fix association hangs due to partial delivery errors
In sctp_ulpq_tail_data(), use return values 0,1 to indicate whether a complete event (with MSG_EOR set) was delivered. A return value of -ENOMEM continues to indicate an out-of-memory condition was encountered. In sctp_ulpq_retrieve_partial() and sctp_ulpq_retrieve_first(), correct message reassembly logic for SCTP partial delivery. Change logic to ensure that as much data as possible is sent with the initial partial delivery and that following partial deliveries contain all available data. In sctp_ulpq_partial_delivery(), attempt partial delivery only if the data on the head of the reassembly queue is at or before the cumulative TSN ACK point. In sctp_ulpq_renege(), use the modified return values from sctp_ulpq_tail_data() to choose whether to attempt partial delivery or to attempt to drain the reassembly queue as a means to reduce memory pressure. Remove call to sctp_tsnmap_mark(), as this is handled correctly in call to sctp_ulpq_tail_data(). Signed-off-by: Lee A. Roberts <lee.roberts@hp.com> Acked-by: Vlad Yasevich <vyasevich@gmail.com> Acked-by: Neil Horman <nhorman@tuxdriver.com>
1 parent 95ac7b8 commit d003b41

1 file changed

Lines changed: 43 additions & 11 deletions

File tree

net/sctp/ulpqueue.c

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ int sctp_ulpq_tail_data(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk,
106106
{
107107
struct sk_buff_head temp;
108108
struct sctp_ulpevent *event;
109+
int event_eor = 0;
109110

110111
/* Create an event from the incoming chunk. */
111112
event = sctp_ulpevent_make_rcvmsg(chunk->asoc, chunk, gfp);
@@ -127,10 +128,12 @@ int sctp_ulpq_tail_data(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk,
127128
/* Send event to the ULP. 'event' is the sctp_ulpevent for
128129
* very first SKB on the 'temp' list.
129130
*/
130-
if (event)
131+
if (event) {
132+
event_eor = (event->msg_flags & MSG_EOR) ? 1 : 0;
131133
sctp_ulpq_tail_event(ulpq, event);
134+
}
132135

133-
return 0;
136+
return event_eor;
134137
}
135138

136139
/* Add a new event for propagation to the ULP. */
@@ -540,14 +543,19 @@ static struct sctp_ulpevent *sctp_ulpq_retrieve_partial(struct sctp_ulpq *ulpq)
540543
ctsn = cevent->tsn;
541544

542545
switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) {
546+
case SCTP_DATA_FIRST_FRAG:
547+
if (!first_frag)
548+
return NULL;
549+
goto done;
543550
case SCTP_DATA_MIDDLE_FRAG:
544551
if (!first_frag) {
545552
first_frag = pos;
546553
next_tsn = ctsn + 1;
547554
last_frag = pos;
548-
} else if (next_tsn == ctsn)
555+
} else if (next_tsn == ctsn) {
549556
next_tsn++;
550-
else
557+
last_frag = pos;
558+
} else
551559
goto done;
552560
break;
553561
case SCTP_DATA_LAST_FRAG:
@@ -651,6 +659,14 @@ static struct sctp_ulpevent *sctp_ulpq_retrieve_first(struct sctp_ulpq *ulpq)
651659
} else
652660
goto done;
653661
break;
662+
663+
case SCTP_DATA_LAST_FRAG:
664+
if (!first_frag)
665+
return NULL;
666+
else
667+
goto done;
668+
break;
669+
654670
default:
655671
return NULL;
656672
}
@@ -1025,16 +1041,28 @@ void sctp_ulpq_partial_delivery(struct sctp_ulpq *ulpq,
10251041
struct sctp_ulpevent *event;
10261042
struct sctp_association *asoc;
10271043
struct sctp_sock *sp;
1044+
__u32 ctsn;
1045+
struct sk_buff *skb;
10281046

10291047
asoc = ulpq->asoc;
10301048
sp = sctp_sk(asoc->base.sk);
10311049

10321050
/* If the association is already in Partial Delivery mode
1033-
* we have noting to do.
1051+
* we have nothing to do.
10341052
*/
10351053
if (ulpq->pd_mode)
10361054
return;
10371055

1056+
/* Data must be at or below the Cumulative TSN ACK Point to
1057+
* start partial delivery.
1058+
*/
1059+
skb = skb_peek(&asoc->ulpq.reasm);
1060+
if (skb != NULL) {
1061+
ctsn = sctp_skb2event(skb)->tsn;
1062+
if (!TSN_lte(ctsn, sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map)))
1063+
return;
1064+
}
1065+
10381066
/* If the user enabled fragment interleave socket option,
10391067
* multiple associations can enter partial delivery.
10401068
* Otherwise, we can only enter partial delivery if the
@@ -1077,12 +1105,16 @@ void sctp_ulpq_renege(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk,
10771105
}
10781106
/* If able to free enough room, accept this chunk. */
10791107
if (chunk && (freed >= needed)) {
1080-
__u32 tsn;
1081-
tsn = ntohl(chunk->subh.data_hdr->tsn);
1082-
sctp_tsnmap_mark(&asoc->peer.tsn_map, tsn, chunk->transport);
1083-
sctp_ulpq_tail_data(ulpq, chunk, gfp);
1084-
1085-
sctp_ulpq_partial_delivery(ulpq, gfp);
1108+
int retval;
1109+
retval = sctp_ulpq_tail_data(ulpq, chunk, gfp);
1110+
/*
1111+
* Enter partial delivery if chunk has not been
1112+
* delivered; otherwise, drain the reassembly queue.
1113+
*/
1114+
if (retval <= 0)
1115+
sctp_ulpq_partial_delivery(ulpq, gfp);
1116+
else if (retval == 1)
1117+
sctp_ulpq_reasm_drain(ulpq);
10861118
}
10871119

10881120
sk_mem_reclaim(asoc->base.sk);

0 commit comments

Comments
 (0)