Date: Thu, 11 Aug 2016 10:14:03 +0000 (UTC) From: Michael Tuexen <tuexen@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org Subject: svn commit: r303956 - stable/11/sys/netinet Message-ID: <201608111014.u7BAE3Pd035944@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: tuexen Date: Thu Aug 11 10:14:03 2016 New Revision: 303956 URL: https://svnweb.freebsd.org/changeset/base/303956 Log: MFC r303792: Fix various bugs in relation to the I-DATA chunk support This is joint work with rrs. MFC r303793: Mark an unused parameter as such. MFC r303798: Don't modify a structure without holding a reference count on it. MFC r303813: Remove stream queue entry consistently from wheel. While there, improve the handling of drain. MFC r303819: Consistently check for unsent data on the stream queues. MFC r303831: Fix a locking issue found by stress testing with tsctp. The inp read lock neeeds to be held when considering control->do_not_ref_stcb. MFC r303834: Fix the sending of FORWARD-TSN and I-FORWARD-TSN chunks. The last SID/SSN pair wasn't filled in. Thanks to Julian Cordes for providing a packetdrill script triggering the issue and making me aware of the bug. Approved by: re (kib) Modified: stable/11/sys/netinet/sctp_indata.c stable/11/sys/netinet/sctp_input.c stable/11/sys/netinet/sctp_output.c stable/11/sys/netinet/sctp_pcb.c stable/11/sys/netinet/sctp_ss_functions.c stable/11/sys/netinet/sctp_structs.h stable/11/sys/netinet/sctp_usrreq.c stable/11/sys/netinet/sctputil.c Directory Properties: stable/11/ (props changed) Modified: stable/11/sys/netinet/sctp_indata.c ============================================================================== --- stable/11/sys/netinet/sctp_indata.c Thu Aug 11 10:10:10 2016 (r303955) +++ stable/11/sys/netinet/sctp_indata.c Thu Aug 11 10:14:03 2016 (r303956) @@ -64,7 +64,7 @@ sctp_add_chk_to_control(struct sctp_queu struct sctp_stream_in *strm, struct sctp_tcb *stcb, struct sctp_association *asoc, - struct sctp_tmit_chunk *chk); + struct sctp_tmit_chunk *chk, int lock_held); void @@ -448,7 +448,7 @@ sctp_abort_in_reasm(struct sctp_tcb *stc } static void -clean_up_control(struct sctp_tcb *stcb, struct sctp_queued_to_read *control) +sctp_clean_up_control(struct sctp_tcb *stcb, struct sctp_queued_to_read *control) { /* * The control could not be placed and must be cleaned. @@ -612,7 +612,7 @@ protocol_error: snprintf(msg, sizeof(msg), "Queue to str msg_id: %u duplicate", control->msg_id); - clean_up_control(stcb, control); + sctp_clean_up_control(stcb, control); op_err = sctp_generate_cause(SCTP_CAUSE_PROTOCOL_VIOLATION, msg); stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_INDATA + SCTP_LOC_3; sctp_abort_an_association(stcb->sctp_ep, stcb, op_err, SCTP_SO_NOT_LOCKED); @@ -739,9 +739,28 @@ sctp_build_readq_entry_from_ctl(struct s nc->port_from = control->port_from; } +static void +sctp_reset_a_control(struct sctp_queued_to_read *control, + struct sctp_inpcb *inp, uint32_t tsn) +{ + control->fsn_included = tsn; + if (control->on_read_q) { + /* + * We have to purge it from there, hopefully this will work + * :-) + */ + TAILQ_REMOVE(&inp->read_queue, control, next); + control->on_read_q = 0; + } +} + static int -sctp_handle_old_data(struct sctp_tcb *stcb, struct sctp_association *asoc, struct sctp_stream_in *strm, - struct sctp_queued_to_read *control, uint32_t pd_point) +sctp_handle_old_unordered_data(struct sctp_tcb *stcb, + struct sctp_association *asoc, + struct sctp_stream_in *strm, + struct sctp_queued_to_read *control, + uint32_t pd_point, + int inp_read_lock_held) { /* * Special handling for the old un-ordered data chunk. All the @@ -774,7 +793,7 @@ restart: } memset(nc, 0, sizeof(struct sctp_queued_to_read)); TAILQ_REMOVE(&control->reasm, chk, sctp_next); - sctp_add_chk_to_control(control, strm, stcb, asoc, chk); + sctp_add_chk_to_control(control, strm, stcb, asoc, chk, SCTP_READ_LOCK_NOT_HELD); fsn++; cnt_added++; chk = NULL; @@ -793,6 +812,8 @@ restart: nc->first_frag_seen = 1; nc->fsn_included = tchk->rec.data.fsn_num; nc->data = tchk->data; + nc->sinfo_ppid = tchk->rec.data.payloadtype; + nc->sinfo_tsn = tchk->rec.data.TSN_seq; sctp_mark_non_revokable(asoc, tchk->rec.data.TSN_seq); tchk->data = NULL; sctp_free_a_chunk(stcb, tchk, SCTP_SO_NOT_LOCKED); @@ -828,7 +849,7 @@ restart: if (control->on_read_q == 0) { sctp_add_to_readq(stcb->sctp_ep, stcb, control, &stcb->sctp_socket->so_rcv, control->end_added, - SCTP_READ_LOCK_NOT_HELD, SCTP_SO_NOT_LOCKED); + inp_read_lock_held, SCTP_SO_NOT_LOCKED); } sctp_wakeup_the_read_socket(stcb->sctp_ep, stcb, SCTP_SO_NOT_LOCKED); if ((nc->first_frag_seen) && !TAILQ_EMPTY(&nc->reasm)) { @@ -839,7 +860,9 @@ restart: control = nc; goto restart; } else { - sctp_free_a_readq(stcb, nc); + if (nc->on_strm_q == 0) { + sctp_free_a_readq(stcb, nc); + } } return (1); } else { @@ -855,7 +878,7 @@ restart: control->pdapi_started = 1; sctp_add_to_readq(stcb->sctp_ep, stcb, control, &stcb->sctp_socket->so_rcv, control->end_added, - SCTP_READ_LOCK_NOT_HELD, SCTP_SO_NOT_LOCKED); + inp_read_lock_held, SCTP_SO_NOT_LOCKED); sctp_wakeup_the_read_socket(stcb->sctp_ep, stcb, SCTP_SO_NOT_LOCKED); return (0); } else { @@ -864,13 +887,14 @@ restart: } static void -sctp_inject_old_data_unordered(struct sctp_tcb *stcb, struct sctp_association *asoc, +sctp_inject_old_unordered_data(struct sctp_tcb *stcb, + struct sctp_association *asoc, struct sctp_queued_to_read *control, struct sctp_tmit_chunk *chk, int *abort_flag) { struct sctp_tmit_chunk *at; - int inserted = 0; + int inserted; /* * Here we need to place the chunk into the control structure sorted @@ -926,18 +950,29 @@ sctp_inject_old_data_unordered(struct sc tdata = control->data; control->data = chk->data; chk->data = tdata; - /* Swap the lengths */ - tmp = control->length; - control->length = chk->send_size; - chk->send_size = tmp; + /* Save the lengths */ + chk->send_size = control->length; + /* Recompute length of control and tail pointer */ + sctp_setup_tail_pointer(control); /* Fix the FSN included */ tmp = control->fsn_included; control->fsn_included = chk->rec.data.fsn_num; chk->rec.data.fsn_num = tmp; + /* Fix the TSN included */ + tmp = control->sinfo_tsn; + control->sinfo_tsn = chk->rec.data.TSN_seq; + chk->rec.data.TSN_seq = tmp; + /* Fix the PPID included */ + tmp = control->sinfo_ppid; + control->sinfo_ppid = chk->rec.data.payloadtype; + chk->rec.data.payloadtype = tmp; + /* Fix tail pointer */ goto place_chunk; } control->first_frag_seen = 1; control->top_fsn = control->fsn_included = chk->rec.data.fsn_num; + control->sinfo_tsn = chk->rec.data.TSN_seq; + control->sinfo_ppid = chk->rec.data.payloadtype; control->data = chk->data; sctp_mark_non_revokable(asoc, chk->rec.data.TSN_seq); chk->data = NULL; @@ -946,12 +981,7 @@ sctp_inject_old_data_unordered(struct sc return; } place_chunk: - if (TAILQ_EMPTY(&control->reasm)) { - TAILQ_INSERT_TAIL(&control->reasm, chk, sctp_next); - asoc->size_on_reasm_queue += chk->send_size; - sctp_ucount_incr(asoc->cnt_on_reasm_queue); - return; - } + inserted = 0; TAILQ_FOREACH(at, &control->reasm, sctp_next) { if (SCTP_TSN_GT(at->rec.data.fsn_num, chk->rec.data.fsn_num)) { /* @@ -985,7 +1015,8 @@ place_chunk: } static int -sctp_deliver_reasm_check(struct sctp_tcb *stcb, struct sctp_association *asoc, struct sctp_stream_in *strm) +sctp_deliver_reasm_check(struct sctp_tcb *stcb, struct sctp_association *asoc, + struct sctp_stream_in *strm, int inp_read_lock_held) { /* * Given a stream, strm, see if any of the SSN's on it that are @@ -1005,10 +1036,11 @@ sctp_deliver_reasm_check(struct sctp_tcb pd_point = stcb->sctp_ep->partial_delivery_point; } control = TAILQ_FIRST(&strm->uno_inqueue); + if ((control) && (asoc->idata_supported == 0)) { /* Special handling needed for "old" data format */ - if (sctp_handle_old_data(stcb, asoc, strm, control, pd_point)) { + if (sctp_handle_old_unordered_data(stcb, asoc, strm, control, pd_point, inp_read_lock_held)) { goto done_un; } } @@ -1037,7 +1069,7 @@ sctp_deliver_reasm_check(struct sctp_tcb sctp_add_to_readq(stcb->sctp_ep, stcb, control, &stcb->sctp_socket->so_rcv, control->end_added, - SCTP_READ_LOCK_NOT_HELD, SCTP_SO_NOT_LOCKED); + inp_read_lock_held, SCTP_SO_NOT_LOCKED); } } else { /* Can we do a PD-API for this un-ordered guy? */ @@ -1047,7 +1079,7 @@ sctp_deliver_reasm_check(struct sctp_tcb sctp_add_to_readq(stcb->sctp_ep, stcb, control, &stcb->sctp_socket->so_rcv, control->end_added, - SCTP_READ_LOCK_NOT_HELD, SCTP_SO_NOT_LOCKED); + inp_read_lock_held, SCTP_SO_NOT_LOCKED); break; } @@ -1096,7 +1128,7 @@ done_un: sctp_add_to_readq(stcb->sctp_ep, stcb, control, &stcb->sctp_socket->so_rcv, control->end_added, - SCTP_READ_LOCK_NOT_HELD, SCTP_SO_NOT_LOCKED); + inp_read_lock_held, SCTP_SO_NOT_LOCKED); } control = nctl; } @@ -1160,7 +1192,7 @@ deliver_more: sctp_add_to_readq(stcb->sctp_ep, stcb, control, &stcb->sctp_socket->so_rcv, control->end_added, - SCTP_READ_LOCK_NOT_HELD, SCTP_SO_NOT_LOCKED); + inp_read_lock_held, SCTP_SO_NOT_LOCKED); } strm->last_sequence_delivered = next_to_del; if (done) { @@ -1177,11 +1209,12 @@ out: return (ret); } + void sctp_add_chk_to_control(struct sctp_queued_to_read *control, struct sctp_stream_in *strm, struct sctp_tcb *stcb, struct sctp_association *asoc, - struct sctp_tmit_chunk *chk) + struct sctp_tmit_chunk *chk, int hold_rlock) { /* * Given a control and a chunk, merge the data from the chk onto the @@ -1189,7 +1222,7 @@ sctp_add_chk_to_control(struct sctp_queu */ int i_locked = 0; - if (control->on_read_q) { + if (control->on_read_q && (hold_rlock == 0)) { /* * Its being pd-api'd so we must do some locks. */ @@ -1271,7 +1304,7 @@ sctp_queue_data_for_reasm(struct sctp_tc if (created_control) { if (sctp_place_control_in_stream(strm, asoc, control)) { /* Duplicate SSN? */ - clean_up_control(stcb, control); + sctp_clean_up_control(stcb, control); sctp_abort_in_reasm(stcb, control, chk, abort_flag, SCTP_FROM_SCTP_INDATA + SCTP_LOC_6); @@ -1292,7 +1325,7 @@ sctp_queue_data_for_reasm(struct sctp_tc } } if ((asoc->idata_supported == 0) && (unordered == 1)) { - sctp_inject_old_data_unordered(stcb, asoc, control, chk, abort_flag); + sctp_inject_old_unordered_data(stcb, asoc, control, chk, abort_flag); return; } /* @@ -1482,7 +1515,7 @@ sctp_queue_data_for_reasm(struct sctp_tc at->rec.data.fsn_num, next_fsn, control->fsn_included); TAILQ_REMOVE(&control->reasm, at, sctp_next); - sctp_add_chk_to_control(control, strm, stcb, asoc, at); + sctp_add_chk_to_control(control, strm, stcb, asoc, at, SCTP_READ_LOCK_NOT_HELD); if (control->on_read_q) { do_wakeup = 1; } @@ -1513,7 +1546,7 @@ sctp_queue_data_for_reasm(struct sctp_tc } static struct sctp_queued_to_read * -find_reasm_entry(struct sctp_stream_in *strm, uint32_t msg_id, int ordered, int old) +sctp_find_reasm_entry(struct sctp_stream_in *strm, uint32_t msg_id, int ordered, int old) { struct sctp_queued_to_read *control; @@ -1573,6 +1606,7 @@ sctp_process_a_data_chunk(struct sctp_tc clen = sizeof(struct sctp_idata_chunk); tsn = ntohl(ch->dp.tsn); msg_id = ntohl(nch->dp.msg_id); + protocol_id = nch->dp.ppid_fsn.protocol_id; if (ch->ch.chunk_flags & SCTP_DATA_FIRST_FRAG) fsn = 0; else @@ -1582,6 +1616,7 @@ sctp_process_a_data_chunk(struct sctp_tc ch = (struct sctp_data_chunk *)sctp_m_getptr(*m, offset, sizeof(struct sctp_data_chunk), (uint8_t *) & chunk_buf); tsn = ntohl(ch->dp.tsn); + protocol_id = ch->dp.protocol_id; clen = sizeof(struct sctp_data_chunk); fsn = tsn; msg_id = (uint32_t) (ntohs(ch->dp.stream_sequence)); @@ -1602,7 +1637,6 @@ sctp_process_a_data_chunk(struct sctp_tc if ((chunk_flags & SCTP_DATA_SACK_IMMEDIATELY) == SCTP_DATA_SACK_IMMEDIATELY) { asoc->send_sack = 1; } - protocol_id = ch->dp.protocol_id; ordered = ((chunk_flags & SCTP_DATA_UNORDERED) == 0); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MAP_LOGGING_ENABLE) { sctp_log_map(tsn, asoc->cumulative_tsn, asoc->highest_tsn_inside_map, SCTP_MAP_TSN_ENTERS); @@ -1722,7 +1756,7 @@ sctp_process_a_data_chunk(struct sctp_tc } if ((chunk_flags & SCTP_DATA_NOT_FRAG) != SCTP_DATA_NOT_FRAG) { /* See if we can find the re-assembly entity */ - control = find_reasm_entry(strm, msg_id, ordered, old_data); + control = sctp_find_reasm_entry(strm, msg_id, ordered, old_data); SCTPDBG(SCTP_DEBUG_XXX, "chunk_flags:0x%x look for control on queues %p\n", chunk_flags, control); if (control) { @@ -1758,7 +1792,7 @@ sctp_process_a_data_chunk(struct sctp_tc */ SCTPDBG(SCTP_DEBUG_XXX, "chunk_flags:0x%x look for msg in case we have dup\n", chunk_flags); - if (find_reasm_entry(strm, msg_id, ordered, old_data)) { + if (sctp_find_reasm_entry(strm, msg_id, ordered, old_data)) { SCTPDBG(SCTP_DEBUG_XXX, "chunk_flags: 0x%x dup detected on msg_id: %u\n", chunk_flags, msg_id); @@ -2179,12 +2213,12 @@ finish_express_del: * Now service re-assembly to pick up anything that has been * held on reassembly queue? */ - (void)sctp_deliver_reasm_check(stcb, asoc, strm); + (void)sctp_deliver_reasm_check(stcb, asoc, strm, SCTP_READ_LOCK_NOT_HELD); need_reasm_check = 0; } if (need_reasm_check) { /* Another one waits ? */ - (void)sctp_deliver_reasm_check(stcb, asoc, strm); + (void)sctp_deliver_reasm_check(stcb, asoc, strm, SCTP_READ_LOCK_NOT_HELD); } return (1); } @@ -4152,28 +4186,8 @@ again: if ((asoc->stream_queue_cnt == 1) && ((asoc->state & SCTP_STATE_SHUTDOWN_PENDING) || (asoc->state & SCTP_STATE_SHUTDOWN_RECEIVED)) && - (asoc->locked_on_sending) - ) { - struct sctp_stream_queue_pending *sp; - - /* - * I may be in a state where we got all across.. but - * cannot write more due to a shutdown... we abort - * since the user did not indicate EOR in this case. - * The sp will be cleaned during free of the asoc. - */ - sp = TAILQ_LAST(&((asoc->locked_on_sending)->outqueue), - sctp_streamhead); - if ((sp) && (sp->length == 0)) { - /* Let cleanup code purge it */ - if (sp->msg_is_complete) { - asoc->stream_queue_cnt--; - } else { - asoc->state |= SCTP_STATE_PARTIAL_MSG_LEFT; - asoc->locked_on_sending = NULL; - asoc->stream_queue_cnt--; - } - } + ((*asoc->ss_functions.sctp_ss_is_user_msgs_incomplete) (stcb, asoc))) { + asoc->state |= SCTP_STATE_PARTIAL_MSG_LEFT; } if ((asoc->state & SCTP_STATE_SHUTDOWN_PENDING) && (asoc->stream_queue_cnt == 0)) { @@ -4868,26 +4882,8 @@ hopeless_peer: if ((asoc->stream_queue_cnt == 1) && ((asoc->state & SCTP_STATE_SHUTDOWN_PENDING) || (asoc->state & SCTP_STATE_SHUTDOWN_RECEIVED)) && - (asoc->locked_on_sending) - ) { - struct sctp_stream_queue_pending *sp; - - /* - * I may be in a state where we got all across.. but - * cannot write more due to a shutdown... we abort - * since the user did not indicate EOR in this case. - */ - sp = TAILQ_LAST(&((asoc->locked_on_sending)->outqueue), - sctp_streamhead); - if ((sp) && (sp->length == 0)) { - asoc->locked_on_sending = NULL; - if (sp->msg_is_complete) { - asoc->stream_queue_cnt--; - } else { - asoc->state |= SCTP_STATE_PARTIAL_MSG_LEFT; - asoc->stream_queue_cnt--; - } - } + ((*asoc->ss_functions.sctp_ss_is_user_msgs_incomplete) (stcb, asoc))) { + asoc->state |= SCTP_STATE_PARTIAL_MSG_LEFT; } if ((asoc->state & SCTP_STATE_SHUTDOWN_PENDING) && (asoc->stream_queue_cnt == 0)) { @@ -5215,7 +5211,7 @@ sctp_kick_prsctp_reorder_queue(struct sc if (need_reasm_check) { int ret; - ret = sctp_deliver_reasm_check(stcb, &stcb->asoc, strmin); + ret = sctp_deliver_reasm_check(stcb, &stcb->asoc, strmin, SCTP_READ_LOCK_HELD); if (SCTP_MSGID_GT(old, tt, strmin->last_sequence_delivered)) { /* Restore the next to deliver unless we are ahead */ strmin->last_sequence_delivered = tt; @@ -5279,19 +5275,21 @@ sctp_kick_prsctp_reorder_queue(struct sc } } if (need_reasm_check) { - (void)sctp_deliver_reasm_check(stcb, &stcb->asoc, strmin); + (void)sctp_deliver_reasm_check(stcb, &stcb->asoc, strmin, SCTP_READ_LOCK_HELD); } } + static void sctp_flush_reassm_for_str_seq(struct sctp_tcb *stcb, struct sctp_association *asoc, - uint16_t stream, uint32_t seq, int ordered, int old) + uint16_t stream, uint32_t seq, int ordered, int old, uint32_t cumtsn) { struct sctp_queued_to_read *control; struct sctp_stream_in *strm; struct sctp_tmit_chunk *chk, *nchk; + int cnt_removed = 0; /* * For now large messages held on the stream reasm that are complete @@ -5302,13 +5300,19 @@ sctp_flush_reassm_for_str_seq(struct sct * queue. */ strm = &asoc->strmin[stream]; - control = find_reasm_entry(strm, (uint32_t) seq, ordered, old); + control = sctp_find_reasm_entry(strm, (uint32_t) seq, ordered, old); if (control == NULL) { /* Not found */ return; } TAILQ_FOREACH_SAFE(chk, &control->reasm, sctp_next, nchk) { /* Purge hanging chunks */ + if (old && (ordered == 0)) { + if (SCTP_TSN_GT(chk->rec.data.TSN_seq, cumtsn)) { + break; + } + } + cnt_removed++; TAILQ_REMOVE(&control->reasm, chk, sctp_next); asoc->size_on_reasm_queue -= chk->send_size; sctp_ucount_decr(asoc->cnt_on_reasm_queue); @@ -5318,7 +5322,35 @@ sctp_flush_reassm_for_str_seq(struct sct } sctp_free_a_chunk(stcb, chk, SCTP_SO_NOT_LOCKED); } - TAILQ_REMOVE(&strm->inqueue, control, next_instrm); + if (!TAILQ_EMPTY(&control->reasm)) { + /* This has to be old data, unordered */ + if (control->data) { + sctp_m_freem(control->data); + control->data = NULL; + } + sctp_reset_a_control(control, stcb->sctp_ep, cumtsn); + chk = TAILQ_FIRST(&control->reasm); + if (chk->rec.data.rcv_flags & SCTP_DATA_FIRST_FRAG) { + TAILQ_REMOVE(&control->reasm, chk, sctp_next); + sctp_add_chk_to_control(control, strm, stcb, asoc, + chk, SCTP_READ_LOCK_HELD); + } + sctp_deliver_reasm_check(stcb, asoc, strm, SCTP_READ_LOCK_HELD); + return; + } + if (control->on_strm_q == SCTP_ON_ORDERED) { + TAILQ_REMOVE(&strm->inqueue, control, next_instrm); + control->on_strm_q = 0; + } else if (control->on_strm_q == SCTP_ON_UNORDERED) { + TAILQ_REMOVE(&strm->uno_inqueue, control, next_instrm); + control->on_strm_q = 0; +#ifdef INVARIANTS + } else if (control->on_strm_q) { + panic("strm: %p ctl: %p unknown %d", + strm, control, control->on_strm_q); +#endif + } + control->on_strm_q = 0; if (control->on_read_q == 0) { sctp_free_remote_addr(control->whoFrom); if (control->data) { @@ -5329,7 +5361,6 @@ sctp_flush_reassm_for_str_seq(struct sct } } - void sctp_handle_forward_tsn(struct sctp_tcb *stcb, struct sctp_forward_tsn_chunk *fwd, @@ -5423,7 +5454,16 @@ sctp_handle_forward_tsn(struct sctp_tcb /*************************************************************/ /* This is now done as part of clearing up the stream/seq */ + if (asoc->idata_supported == 0) { + uint16_t sid; + /* Flush all the un-ordered data based on cum-tsn */ + SCTP_INP_READ_LOCK(stcb->sctp_ep); + for (sid = 0; sid < asoc->streamincnt; sid++) { + sctp_flush_reassm_for_str_seq(stcb, asoc, sid, 0, 0, 1, new_cum_tsn); + } + SCTP_INP_READ_UNLOCK(stcb->sctp_ep); + } /*******************************************************/ /* 3. Update the PR-stream re-ordering queues and fix */ /* delivery issues as needed. */ @@ -5502,7 +5542,19 @@ sctp_handle_forward_tsn(struct sctp_tcb asoc->fragmented_delivery_inprogress = 0; } strm = &asoc->strmin[stream]; - sctp_flush_reassm_for_str_seq(stcb, asoc, stream, sequence, ordered, old); + if (asoc->idata_supported == 0) { + uint16_t strm_at; + + for (strm_at = strm->last_sequence_delivered; SCTP_MSGID_GE(1, sequence, strm_at); strm_at++) { + sctp_flush_reassm_for_str_seq(stcb, asoc, stream, strm_at, ordered, old, new_cum_tsn); + } + } else { + uint32_t strm_at; + + for (strm_at = strm->last_sequence_delivered; SCTP_MSGID_GE(0, sequence, strm_at); strm_at++) { + sctp_flush_reassm_for_str_seq(stcb, asoc, stream, strm_at, ordered, old, new_cum_tsn); + } + } TAILQ_FOREACH(ctl, &stcb->sctp_ep->read_queue, next) { if ((ctl->sinfo_stream == stream) && (ctl->sinfo_ssn == sequence)) { Modified: stable/11/sys/netinet/sctp_input.c ============================================================================== --- stable/11/sys/netinet/sctp_input.c Thu Aug 11 10:10:10 2016 (r303955) +++ stable/11/sys/netinet/sctp_input.c Thu Aug 11 10:14:03 2016 (r303956) @@ -221,18 +221,18 @@ sctp_is_there_unsent_data(struct sctp_tc #endif ) { - int unsent_data = 0; + int unsent_data; unsigned int i; struct sctp_stream_queue_pending *sp; struct sctp_association *asoc; /* - * This function returns the number of streams that have true unsent - * data on them. Note that as it looks through it will clean up any - * places that have old data that has been sent but left at top of - * stream queue. + * This function returns if any stream has true unsent data on it. + * Note that as it looks through it will clean up any places that + * have old data that has been sent but left at top of stream queue. */ asoc = &stcb->asoc; + unsent_data = 0; SCTP_TCB_SEND_LOCK(stcb); if (!stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) { /* Check to see if some data queued */ @@ -260,6 +260,7 @@ sctp_is_there_unsent_data(struct sctp_tc } atomic_subtract_int(&stcb->asoc.stream_queue_cnt, 1); TAILQ_REMOVE(&stcb->asoc.strmout[i].outqueue, sp, next); + stcb->asoc.ss_functions.sctp_ss_remove_from_stream(stcb, asoc, &asoc->strmout[i], sp, 1); if (sp->net) { sctp_free_remote_addr(sp->net); sp->net = NULL; @@ -269,8 +270,13 @@ sctp_is_there_unsent_data(struct sctp_tc sp->data = NULL; } sctp_free_a_strmoq(stcb, sp, so_locked); + if (!TAILQ_EMPTY(&stcb->asoc.strmout[i].outqueue)) { + unsent_data++; + } } else { unsent_data++; + } + if (unsent_data > 0) { break; } } @@ -341,8 +347,9 @@ sctp_process_init(struct sctp_init_chunk for (i = newcnt; i < asoc->pre_open_streams; i++) { outs = &asoc->strmout[i]; TAILQ_FOREACH_SAFE(sp, &outs->outqueue, next, nsp) { + atomic_subtract_int(&stcb->asoc.stream_queue_cnt, 1); TAILQ_REMOVE(&outs->outqueue, sp, next); - asoc->stream_queue_cnt--; + stcb->asoc.ss_functions.sctp_ss_remove_from_stream(stcb, asoc, outs, sp, 1); sctp_ulp_notify(SCTP_NOTIFY_SPECIAL_SP_FAIL, stcb, 0, sp, SCTP_SO_NOT_LOCKED); if (sp->data) { @@ -1047,7 +1054,7 @@ sctp_handle_shutdown_ack(struct sctp_shu #ifdef INVARIANTS if (!TAILQ_EMPTY(&asoc->send_queue) || !TAILQ_EMPTY(&asoc->sent_queue) || - !stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) { + sctp_is_there_unsent_data(stcb, SCTP_SO_NOT_LOCKED)) { panic("Queues are not empty when handling SHUTDOWN-ACK"); } #endif @@ -3213,7 +3220,7 @@ sctp_handle_shutdown_complete(struct sct #ifdef INVARIANTS if (!TAILQ_EMPTY(&asoc->send_queue) || !TAILQ_EMPTY(&asoc->sent_queue) || - !stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) { + sctp_is_there_unsent_data(stcb, SCTP_SO_NOT_LOCKED)) { panic("Queues are not empty when handling SHUTDOWN-COMPLETE"); } #endif Modified: stable/11/sys/netinet/sctp_output.c ============================================================================== --- stable/11/sys/netinet/sctp_output.c Thu Aug 11 10:10:10 2016 (r303955) +++ stable/11/sys/netinet/sctp_output.c Thu Aug 11 10:14:03 2016 (r303956) @@ -3657,7 +3657,7 @@ sctp_process_cmsgs_for_init(struct sctp_ stcb->asoc.strmout[i].stream_no = i; stcb->asoc.strmout[i].last_msg_incomplete = 0; stcb->asoc.strmout[i].state = SCTP_STREAM_OPENING; - stcb->asoc.ss_functions.sctp_ss_init_stream(&stcb->asoc.strmout[i], NULL); + stcb->asoc.ss_functions.sctp_ss_init_stream(stcb, &stcb->asoc.strmout[i], NULL); } } break; @@ -6687,14 +6687,10 @@ sctp_sendall_iterator(struct sctp_inpcb asoc = &stcb->asoc; if (ca->sndrcv.sinfo_flags & SCTP_EOF) { /* shutdown this assoc */ - int cnt; - - cnt = sctp_is_there_unsent_data(stcb, SCTP_SO_NOT_LOCKED); - if (TAILQ_EMPTY(&asoc->send_queue) && TAILQ_EMPTY(&asoc->sent_queue) && - (cnt == 0)) { - if (asoc->locked_on_sending) { + sctp_is_there_unsent_data(stcb, SCTP_SO_NOT_LOCKED) == 0) { + if ((*asoc->ss_functions.sctp_ss_is_user_msgs_incomplete) (stcb, asoc)) { goto abort_anyway; } /* @@ -6736,18 +6732,8 @@ sctp_sendall_iterator(struct sctp_inpcb if ((SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_SENT) && (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_RECEIVED) && (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_ACK_SENT)) { - if (asoc->locked_on_sending) { - /* - * Locked to send out the - * data - */ - struct sctp_stream_queue_pending *sp; - - sp = TAILQ_LAST(&asoc->locked_on_sending->outqueue, sctp_streamhead); - if (sp) { - if ((sp->length == 0) && (sp->msg_is_complete == 0)) - asoc->state |= SCTP_STATE_PARTIAL_MSG_LEFT; - } + if ((*asoc->ss_functions.sctp_ss_is_user_msgs_incomplete) (stcb, asoc)) { + asoc->state |= SCTP_STATE_PARTIAL_MSG_LEFT; } asoc->state |= SCTP_STATE_SHUTDOWN_PENDING; if (TAILQ_EMPTY(&asoc->send_queue) && @@ -7170,7 +7156,6 @@ sctp_move_to_outqueue(struct sctp_tcb *s struct sctp_stream_out *strq, uint32_t goal_mtu, uint32_t frag_point, - int *locked, int *giveup, int eeor_mode, int *bail, @@ -7196,10 +7181,8 @@ sctp_move_to_outqueue(struct sctp_tcb *s asoc = &stcb->asoc; one_more_time: /* sa_ignore FREED_MEMORY */ - *locked = 0; sp = TAILQ_FIRST(&strq->outqueue); if (sp == NULL) { - *locked = 0; if (send_lock_up == 0) { SCTP_TCB_SEND_LOCK(stcb); send_lock_up = 1; @@ -7245,12 +7228,12 @@ one_more_time: } atomic_subtract_int(&asoc->stream_queue_cnt, 1); TAILQ_REMOVE(&strq->outqueue, sp, next); + stcb->asoc.ss_functions.sctp_ss_remove_from_stream(stcb, asoc, strq, sp, send_lock_up); if ((strq->state == SCTP_STREAM_RESET_PENDING) && (strq->chunks_on_queues == 0) && TAILQ_EMPTY(&strq->outqueue)) { stcb->asoc.trigger_reset = 1; } - stcb->asoc.ss_functions.sctp_ss_remove_from_stream(stcb, asoc, strq, sp, send_lock_up); if (sp->net) { sctp_free_remote_addr(sp->net); sp->net = NULL; @@ -7261,8 +7244,6 @@ one_more_time: } sctp_free_a_strmoq(stcb, sp, so_locked); /* we can't be locked to it */ - *locked = 0; - stcb->asoc.locked_on_sending = NULL; if (send_lock_up) { SCTP_TCB_SEND_UNLOCK(stcb); send_lock_up = 0; @@ -7274,8 +7255,6 @@ one_more_time: * sender just finished this but still holds a * reference */ - if (stcb->asoc.idata_supported == 0) - *locked = 1; *giveup = 1; to_move = 0; goto out_of; @@ -7284,8 +7263,6 @@ one_more_time: /* is there some to get */ if (sp->length == 0) { /* no */ - if (stcb->asoc.idata_supported == 0) - *locked = 1; *giveup = 1; to_move = 0; goto out_of; @@ -7308,8 +7285,6 @@ one_more_time: } sp->length = 0; sp->some_taken = 1; - if (stcb->asoc.idata_supported == 0) - *locked = 1; *giveup = 1; to_move = 0; goto out_of; @@ -7373,10 +7348,6 @@ re_look: } } else { /* Nothing to take. */ - if ((sp->some_taken) && - (stcb->asoc.idata_supported == 0)) { - *locked = 1; - } *giveup = 1; to_move = 0; goto out_of; @@ -7686,7 +7657,6 @@ dont_do_it: } if (sp->msg_is_complete && (sp->length == 0) && (sp->sender_all_done)) { /* All done pull and kill the message */ - atomic_subtract_int(&asoc->stream_queue_cnt, 1); if (sp->put_last_out == 0) { SCTP_PRINTF("Gak, put out entire msg with NO end!-2\n"); SCTP_PRINTF("sender_done:%d len:%d msg_comp:%d put_last_out:%d send_lock:%d\n", @@ -7700,13 +7670,14 @@ dont_do_it: SCTP_TCB_SEND_LOCK(stcb); send_lock_up = 1; } + atomic_subtract_int(&asoc->stream_queue_cnt, 1); TAILQ_REMOVE(&strq->outqueue, sp, next); + stcb->asoc.ss_functions.sctp_ss_remove_from_stream(stcb, asoc, strq, sp, send_lock_up); if ((strq->state == SCTP_STREAM_RESET_PENDING) && (strq->chunks_on_queues == 0) && TAILQ_EMPTY(&strq->outqueue)) { stcb->asoc.trigger_reset = 1; } - stcb->asoc.ss_functions.sctp_ss_remove_from_stream(stcb, asoc, strq, sp, send_lock_up); if (sp->net) { sctp_free_remote_addr(sp->net); sp->net = NULL; @@ -7716,14 +7687,6 @@ dont_do_it: sp->data = NULL; } sctp_free_a_strmoq(stcb, sp, so_locked); - - /* we can't be locked to it */ - *locked = 0; - stcb->asoc.locked_on_sending = NULL; - } else { - /* more to go, we are locked */ - if (stcb->asoc.idata_supported == 0) - *locked = 1; } asoc->chunks_on_out_queue++; strq->chunks_on_queues++; @@ -7748,7 +7711,7 @@ sctp_fill_outqueue(struct sctp_tcb *stcb struct sctp_association *asoc; struct sctp_stream_out *strq; int goal_mtu, moved_how_much, total_moved = 0, bail = 0; - int locked, giveup; + int giveup; SCTP_TCB_LOCK_ASSERT(stcb); asoc = &stcb->asoc; @@ -7777,36 +7740,20 @@ sctp_fill_outqueue(struct sctp_tcb *stcb /* must make even word boundary */ goal_mtu &= 0xfffffffc; - if (asoc->locked_on_sending) { - /* We are stuck on one stream until the message completes. */ - strq = asoc->locked_on_sending; - locked = 1; - } else { - strq = stcb->asoc.ss_functions.sctp_ss_select_stream(stcb, net, asoc); - locked = 0; - } + strq = stcb->asoc.ss_functions.sctp_ss_select_stream(stcb, net, asoc); while ((goal_mtu > 0) && strq) { giveup = 0; bail = 0; - moved_how_much = sctp_move_to_outqueue(stcb, strq, goal_mtu, frag_point, &locked, + moved_how_much = sctp_move_to_outqueue(stcb, strq, goal_mtu, frag_point, &giveup, eeor_mode, &bail, so_locked); - if (moved_how_much) - stcb->asoc.ss_functions.sctp_ss_scheduled(stcb, net, asoc, strq, moved_how_much); + stcb->asoc.ss_functions.sctp_ss_scheduled(stcb, net, asoc, strq, moved_how_much); - if (locked) { - asoc->locked_on_sending = strq; - if ((moved_how_much == 0) || (giveup) || bail) - /* no more to move for now */ - break; - } else { - asoc->locked_on_sending = NULL; - if ((giveup) || bail) { - break; - } - strq = stcb->asoc.ss_functions.sctp_ss_select_stream(stcb, net, asoc); - if (strq == NULL) { - break; - } + if ((giveup) || bail) { + break; + } + strq = stcb->asoc.ss_functions.sctp_ss_select_stream(stcb, net, asoc); + if (strq == NULL) { + break; } total_moved += moved_how_much; goal_mtu -= (moved_how_much + sizeof(struct sctp_data_chunk)); @@ -7951,7 +7898,7 @@ sctp_med_chunk_output(struct sctp_inpcb (asoc->ctrl_queue_cnt == stcb->asoc.ecn_echo_cnt_onq)) && TAILQ_EMPTY(&asoc->asconf_send_queue) && TAILQ_EMPTY(&asoc->send_queue) && - stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) { + sctp_is_there_unsent_data(stcb, so_locked) == 0) { nothing_to_send: *reason_code = 9; return (0); @@ -10227,15 +10174,14 @@ do_it_again: un_sent = ((stcb->asoc.total_output_queue_size - stcb->asoc.total_flight) + (stcb->asoc.stream_queue_cnt * sizeof(struct sctp_data_chunk))); if ((un_sent < (int)(stcb->asoc.smallest_mtu - SCTP_MIN_OVERHEAD)) && - (stcb->asoc.total_flight > 0) && - ((stcb->asoc.locked_on_sending == NULL) || - sctp_is_feature_on(inp, SCTP_PCB_FLAGS_EXPLICIT_EOR))) { + (stcb->asoc.total_flight > 0)) { +/* && sctp_is_feature_on(inp, SCTP_PCB_FLAGS_EXPLICIT_EOR))) {*/ break; } } if (TAILQ_EMPTY(&asoc->control_send_queue) && TAILQ_EMPTY(&asoc->send_queue) && - stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) { + sctp_is_there_unsent_data(stcb, so_locked) == 0) { /* Nothing left to send */ break; } @@ -10312,9 +10258,14 @@ void send_forward_tsn(struct sctp_tcb *stcb, struct sctp_association *asoc) { - struct sctp_tmit_chunk *chk; + struct sctp_tmit_chunk *chk, *at, *tp1, *last; struct sctp_forward_tsn_chunk *fwdtsn; + struct sctp_strseq *strseq; + struct sctp_strseq_mid *strseq_m; uint32_t advance_peer_ack_point; + unsigned int cnt_of_space, i, ovh; + unsigned int space_needed; + unsigned int cnt_of_skipped = 0; int old; if (asoc->idata_supported) { @@ -10369,165 +10320,155 @@ sctp_fill_in_rest: * stream/seq of the ones we skip. */ SCTP_BUF_LEN(chk->data) = 0; - { - struct sctp_tmit_chunk *at, *tp1, *last; - struct sctp_strseq *strseq; - struct sctp_strseq_mid *strseq_m; - unsigned int cnt_of_space, i, ovh; - unsigned int space_needed; - unsigned int cnt_of_skipped = 0; - - TAILQ_FOREACH(at, &asoc->sent_queue, sctp_next) { - if ((at->sent != SCTP_FORWARD_TSN_SKIP) && - (at->sent != SCTP_DATAGRAM_NR_ACKED)) { - /* no more to look at */ - break; - } - if ((at->rec.data.rcv_flags & SCTP_DATA_UNORDERED) && old) { - /* We don't report these */ - continue; - } - cnt_of_skipped++; + TAILQ_FOREACH(at, &asoc->sent_queue, sctp_next) { + if ((at->sent != SCTP_FORWARD_TSN_SKIP) && + (at->sent != SCTP_DATAGRAM_NR_ACKED)) { + /* no more to look at */ + break; } - if (old) { - space_needed = (sizeof(struct sctp_forward_tsn_chunk) + - (cnt_of_skipped * sizeof(struct sctp_strseq))); - } else { - space_needed = (sizeof(struct sctp_forward_tsn_chunk) + - (cnt_of_skipped * sizeof(struct sctp_strseq_mid))); + if (old && (at->rec.data.rcv_flags & SCTP_DATA_UNORDERED)) { + /* We don't report these */ + continue; } - cnt_of_space = (unsigned int)M_TRAILINGSPACE(chk->data); + cnt_of_skipped++; + } + if (old) { + space_needed = (sizeof(struct sctp_forward_tsn_chunk) + + (cnt_of_skipped * sizeof(struct sctp_strseq))); + } else { + space_needed = (sizeof(struct sctp_forward_tsn_chunk) + + (cnt_of_skipped * sizeof(struct sctp_strseq_mid))); + } + cnt_of_space = (unsigned int)M_TRAILINGSPACE(chk->data); - if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) { - ovh = SCTP_MIN_OVERHEAD; - } else { - ovh = SCTP_MIN_V4_OVERHEAD; - } - if (cnt_of_space > (asoc->smallest_mtu - ovh)) { - /* trim to a mtu size */ - cnt_of_space = asoc->smallest_mtu - ovh; - } + if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) { + ovh = SCTP_MIN_OVERHEAD; + } else { + ovh = SCTP_MIN_V4_OVERHEAD; + } + if (cnt_of_space > (asoc->smallest_mtu - ovh)) { + /* trim to a mtu size */ + cnt_of_space = asoc->smallest_mtu - ovh; + } + if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_TRY_ADVANCE) { + sctp_misc_ints(SCTP_FWD_TSN_CHECK, + 0xff, 0, cnt_of_skipped, + asoc->advanced_peer_ack_point); + } + advance_peer_ack_point = asoc->advanced_peer_ack_point; + if (cnt_of_space < space_needed) { + /*- + * ok we must trim down the chunk by lowering the + * advance peer ack point. + */ if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_TRY_ADVANCE) { sctp_misc_ints(SCTP_FWD_TSN_CHECK, - 0xff, 0, cnt_of_skipped, - asoc->advanced_peer_ack_point); - + 0xff, 0xff, cnt_of_space, + space_needed); } - advance_peer_ack_point = asoc->advanced_peer_ack_point; - if (cnt_of_space < space_needed) { - /*- - * ok we must trim down the chunk by lowering the - * advance peer ack point. - */ - if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_TRY_ADVANCE) { - sctp_misc_ints(SCTP_FWD_TSN_CHECK, - 0xff, 0xff, cnt_of_space, - space_needed); - } - if (old) { - cnt_of_skipped = cnt_of_space - sizeof(struct sctp_forward_tsn_chunk); - cnt_of_skipped /= sizeof(struct sctp_strseq); - } else { - cnt_of_skipped = cnt_of_space - sizeof(struct sctp_forward_tsn_chunk); - cnt_of_skipped /= sizeof(struct sctp_strseq_mid); - } - /*- - * Go through and find the TSN that will be the one - * we report. - */ - at = TAILQ_FIRST(&asoc->sent_queue); - if (at != NULL) { *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201608111014.u7BAE3Pd035944>