📄 outqueue.c
字号:
&transport_list); } packet = &transport->packet; sctp_packet_config(packet, vtag, asoc->peer.ecn_capable); } SCTP_DEBUG_PRINTK("sctp_outq_flush(%p, %p[%s]), ", q, chunk, chunk && chunk->chunk_hdr ? sctp_cname(SCTP_ST_CHUNK( chunk->chunk_hdr->type)) : "Illegal Chunk"); SCTP_DEBUG_PRINTK("TX TSN 0x%x skb->head " "%p skb->users %d.\n", ntohl(chunk->subh.data_hdr->tsn), chunk->skb ?chunk->skb->head : NULL, chunk->skb ? atomic_read(&chunk->skb->users) : -1); /* Add the chunk to the packet. */ status = sctp_packet_transmit_chunk(packet, chunk); switch (status) { case SCTP_XMIT_PMTU_FULL: case SCTP_XMIT_RWND_FULL: case SCTP_XMIT_NAGLE_DELAY: /* We could not append this chunk, so put * the chunk back on the output queue. */ SCTP_DEBUG_PRINTK("sctp_outq_flush: could " "not transmit TSN: 0x%x, status: %d\n", ntohl(chunk->subh.data_hdr->tsn), status); sctp_outq_head_data(q, chunk); goto sctp_flush_out; break; case SCTP_XMIT_OK: break; default: BUG(); } /* BUG: We assume that the sctp_packet_transmit() * call below will succeed all the time and add the * chunk to the transmitted list and restart the * timers. * It is possible that the call can fail under OOM * conditions. * * Is this really a problem? Won't this behave * like a lost TSN? */ list_add_tail(&chunk->transmitted_list, &transport->transmitted); sctp_transport_reset_timers(transport); q->empty = 0; /* Only let one DATA chunk get bundled with a * COOKIE-ECHO chunk. */ if (packet->has_cookie_echo) goto sctp_flush_out; } break; default: /* Do nothing. */ break; }sctp_flush_out: /* Before returning, examine all the transports touched in * this call. Right now, we bluntly force clear all the * transports. Things might change after we implement Nagle. * But such an examination is still required. * * --xguo */ while ((ltransport = sctp_list_dequeue(&transport_list)) != NULL ) { struct sctp_transport *t = list_entry(ltransport, struct sctp_transport, send_ready); packet = &t->packet; if (!sctp_packet_empty(packet)) error = sctp_packet_transmit(packet); } return error;}/* Update unack_data based on the incoming SACK chunk */static void sctp_sack_update_unack_data(struct sctp_association *assoc, struct sctp_sackhdr *sack){ sctp_sack_variable_t *frags; __u16 unack_data; int i; unack_data = assoc->next_tsn - assoc->ctsn_ack_point - 1; frags = sack->variable; for (i = 0; i < ntohs(sack->num_gap_ack_blocks); i++) { unack_data -= ((ntohs(frags[i].gab.end) - ntohs(frags[i].gab.start) + 1)); } assoc->unack_data = unack_data;}/* Return the highest new tsn that is acknowledged by the given SACK chunk. */static __u32 sctp_highest_new_tsn(struct sctp_sackhdr *sack, struct sctp_association *asoc){ struct list_head *ltransport, *lchunk; struct sctp_transport *transport; struct sctp_chunk *chunk; __u32 highest_new_tsn, tsn; struct list_head *transport_list = &asoc->peer.transport_addr_list; highest_new_tsn = ntohl(sack->cum_tsn_ack); list_for_each(ltransport, transport_list) { transport = list_entry(ltransport, struct sctp_transport, transports); list_for_each(lchunk, &transport->transmitted) { chunk = list_entry(lchunk, struct sctp_chunk, transmitted_list); tsn = ntohl(chunk->subh.data_hdr->tsn); if (!chunk->tsn_gap_acked && TSN_lt(highest_new_tsn, tsn) && sctp_acked(sack, tsn)) highest_new_tsn = tsn; } } return highest_new_tsn;}/* This is where we REALLY process a SACK. * * Process the SACK against the outqueue. Mostly, this just frees * things off the transmitted queue. */int sctp_outq_sack(struct sctp_outq *q, struct sctp_sackhdr *sack){ struct sctp_association *asoc = q->asoc; struct sctp_transport *transport; struct sctp_chunk *tchunk = NULL; struct list_head *lchunk, *transport_list, *pos, *temp; sctp_sack_variable_t *frags = sack->variable; __u32 sack_ctsn, ctsn, tsn; __u32 highest_tsn, highest_new_tsn; __u32 sack_a_rwnd; unsigned outstanding; struct sctp_transport *primary = asoc->peer.primary_path; int count_of_newacks = 0; /* Grab the association's destination address list. */ transport_list = &asoc->peer.transport_addr_list; sack_ctsn = ntohl(sack->cum_tsn_ack); /* * SFR-CACC algorithm: * On receipt of a SACK the sender SHOULD execute the * following statements. * * 1) If the cumulative ack in the SACK passes next tsn_at_change * on the current primary, the CHANGEOVER_ACTIVE flag SHOULD be * cleared. The CYCLING_CHANGEOVER flag SHOULD also be cleared for * all destinations. */ if (TSN_lte(primary->cacc.next_tsn_at_change, sack_ctsn)) { primary->cacc.changeover_active = 0; list_for_each(pos, transport_list) { transport = list_entry(pos, struct sctp_transport, transports); transport->cacc.cycling_changeover = 0; } } /* * SFR-CACC algorithm: * 2) If the SACK contains gap acks and the flag CHANGEOVER_ACTIVE * is set the receiver of the SACK MUST take the following actions: * * A) Initialize the cacc_saw_newack to 0 for all destination * addresses. */ if (sack->num_gap_ack_blocks && primary->cacc.changeover_active) { list_for_each(pos, transport_list) { transport = list_entry(pos, struct sctp_transport, transports); transport->cacc.cacc_saw_newack = 0; } } /* Get the highest TSN in the sack. */ highest_tsn = sack_ctsn; if (sack->num_gap_ack_blocks) highest_tsn += ntohs(frags[ntohs(sack->num_gap_ack_blocks) - 1].gab.end); if (TSN_lt(asoc->highest_sacked, highest_tsn)) { highest_new_tsn = highest_tsn; asoc->highest_sacked = highest_tsn; } else { highest_new_tsn = sctp_highest_new_tsn(sack, asoc); } /* Run through the retransmit queue. Credit bytes received * and free those chunks that we can. */ sctp_check_transmitted(q, &q->retransmit, NULL, sack, highest_new_tsn); sctp_mark_missing(q, &q->retransmit, NULL, highest_new_tsn, 0); /* Run through the transmitted queue. * Credit bytes received and free those chunks which we can. * * This is a MASSIVE candidate for optimization. */ list_for_each(pos, transport_list) { transport = list_entry(pos, struct sctp_transport, transports); sctp_check_transmitted(q, &transport->transmitted, transport, sack, highest_new_tsn); /* * SFR-CACC algorithm: * C) Let count_of_newacks be the number of * destinations for which cacc_saw_newack is set. */ if (transport->cacc.cacc_saw_newack) count_of_newacks ++; } list_for_each(pos, transport_list) { transport = list_entry(pos, struct sctp_transport, transports); sctp_mark_missing(q, &transport->transmitted, transport, highest_new_tsn, count_of_newacks); } /* Move the Cumulative TSN Ack Point if appropriate. */ if (TSN_lt(asoc->ctsn_ack_point, sack_ctsn)) asoc->ctsn_ack_point = sack_ctsn; /* Update unack_data field in the assoc. */ sctp_sack_update_unack_data(asoc, sack); ctsn = asoc->ctsn_ack_point; /* Throw away stuff rotting on the sack queue. */ list_for_each_safe(lchunk, temp, &q->sacked) { tchunk = list_entry(lchunk, struct sctp_chunk, transmitted_list); tsn = ntohl(tchunk->subh.data_hdr->tsn); if (TSN_lte(tsn, ctsn)) sctp_chunk_free(tchunk); } /* ii) Set rwnd equal to the newly received a_rwnd minus the * number of bytes still outstanding after processing the * Cumulative TSN Ack and the Gap Ack Blocks. */ sack_a_rwnd = ntohl(sack->a_rwnd); outstanding = q->outstanding_bytes; if (outstanding < sack_a_rwnd) sack_a_rwnd -= outstanding; else sack_a_rwnd = 0; asoc->peer.rwnd = sack_a_rwnd; sctp_generate_fwdtsn(q, sack_ctsn); SCTP_DEBUG_PRINTK("%s: sack Cumulative TSN Ack is 0x%x.\n", __FUNCTION__, sack_ctsn); SCTP_DEBUG_PRINTK("%s: Cumulative TSN Ack of association, " "%p is 0x%x. Adv peer ack point: 0x%x\n", __FUNCTION__, asoc, ctsn, asoc->adv_peer_ack_point); /* See if all chunks are acked. * Make sure the empty queue handler will get run later. */ q->empty = (list_empty(&q->out_chunk_list) && list_empty(&q->control_chunk_list) && list_empty(&q->retransmit)); if (!q->empty) goto finish; list_for_each(pos, transport_list) { transport = list_entry(pos, struct sctp_transport, transports); q->empty = q->empty && list_empty(&transport->transmitted); if (!q->empty) goto finish; } SCTP_DEBUG_PRINTK("sack queue is empty.\n");finish: return q->empty;}/* Is the outqueue empty? */int sctp_outq_is_empty(const struct sctp_outq *q){ return q->empty;}/******************************************************************** * 2nd Level Abstractions ********************************************************************//* Go through a transport's transmitted list or the association's retransmit * list and move chunks that are acked by the Cumulative TSN Ack to q->sacked. * The retransmit list will not have an associated transport. * * I added coherent debug information output. --xguo * * Instead of printing 'sacked' or 'kept' for each TSN on the * transmitted_queue, we print a range: SACKED: TSN1-TSN2, TSN3, TSN4-TSN5. * KEPT TSN6-TSN7, etc. */static void sctp_check_transmitted(struct sctp_outq *q, struct list_head *transmitted_queue, struct sctp_transport *transport, struct sctp_sackhdr *sack, __u32 highest_new_tsn_in_sack){ struct list_head *lchunk; struct sctp_chunk *tchunk; struct list_head tlist; __u32 tsn; __u32 sack_ctsn; __u32 rtt; __u8 restart_timer = 0; int bytes_acked = 0; /* These state variables are for coherent debug output. --xguo */#if SCTP_DEBUG __u32 dbg_ack_tsn = 0; /* An ACKed TSN range starts here... */ __u32 dbg_last_ack_tsn = 0; /* ...and finishes here. */ __u32 dbg_kept_tsn = 0; /* An un-ACKed range starts here... */ __u32 dbg_last_kept_tsn = 0; /* ...and finishes here. */ /* 0 : The last TSN was ACKed. * 1 : The last TSN was NOT ACKed (i.e. KEPT). * -1: We need to initialize. */ int dbg_prt_state = -1;#endif /* SCTP_DEBUG */ sack_ctsn = ntohl(sack->cum_tsn_ack); INIT_LIST_HEAD(&tlist); /* The while loop will skip empty transmitted queues. */ while (NULL != (lchunk = sctp_list_dequeue(transmitted_queue))) { tchunk = list_entry(lchunk, struct sctp_chunk, transmitted_list); if (sctp_chunk_abandoned(tchunk)) { /* Move the chunk to abandoned list. */ sctp_insert_list(&q->abandoned, lchunk); /* If this chunk has not been acked, stop * considering it as 'outstanding'. */ if (!tchunk->tsn_gap_acked) { tchunk->transport->flight_size -= sctp_data_size(tchunk); q->outstanding_bytes -= sctp_data_size(tchunk); } continue; } tsn = ntohl(tchunk->subh.data_hdr->tsn); if (sctp_acked(sack, tsn)) { /* If this queue is the retransmit queue, the * retransmit timer has already reclaimed * the outstanding bytes for this chunk, so only * count bytes associated with a transport. */ if (transport) { /* If this chunk is being used for RTT * measurement, calculate the RTT and update * the RTO using this value. * * 6.3.1 C5) Karn's algorithm: RTT measurements * MUST NOT be made using packets that were * retransmitted (and thus for which it is * ambiguous whether the reply was for the * first instance of the packet or a later * instance). */ if (!tchunk->tsn_gap_acked && !tchunk->resent && tchunk->rtt_in_progress) { tchunk->rtt_in_progress = 0; rtt = jiffies - tchunk->sent_at; sctp_transport_update_rto(transport, rtt); } } if (TSN_lte(tsn, sack_ctsn)) { /* RFC 2960 6.3.2 Retransmission Timer Rules * * R3) Whenever a SACK is received * that acknowledges the DATA chunk * with the earliest outstanding TSN * for that address, restart T3-rtx * timer for that address with its * current RTO. */ restart_timer = 1; if (!tchunk->tsn_gap_acked) { tchunk->tsn_gap_acked = 1; bytes_acked += sctp_data_size(tchunk); /* * SFR-CACC algorithm: * 2) If the SACK contains gap acks * and the flag CHANGEOVER_ACTIVE is * set the receiver of the SACK MUST * take the following action: * * B) For each TSN t being acked that * has not been acked in any SACK so * far, set cacc_saw_newack to 1 for * the destination that the TSN was * sent to. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -