Author: rrs
Date: Sat Mar 14 13:42:13 2009
New Revision: 189790
URL: http://svn.freebsd.org/changeset/base/189790

Log:
  Fixes several PR-SCTP releated bugs.
   - When sending large PR-SCTP messages over a
     lossy link we would incorrectly calculate the fwd-tsn
   - When receiving large multipart pr-sctp packets we would
     incorrectly send back a SACK that would renege improperly
     on already received packets thus causing unneeded retransmissions.

Modified:
  head/sys/netinet/sctp.h
  head/sys/netinet/sctp_constants.h
  head/sys/netinet/sctp_indata.c
  head/sys/netinet/sctp_output.c
  head/sys/netinet/sctp_timer.c
  head/sys/netinet/sctp_var.h
  head/sys/netinet/sctputil.c
  head/sys/netinet/sctputil.h

Modified: head/sys/netinet/sctp.h
==============================================================================
--- head/sys/netinet/sctp.h     Sat Mar 14 11:43:38 2009        (r189789)
+++ head/sys/netinet/sctp.h     Sat Mar 14 13:42:13 2009        (r189790)
@@ -544,7 +544,7 @@ struct sctp_error_unrecognized_chunk {
 #define SCTP_THRESHOLD_LOGGING              0x02000000
 #define SCTP_LOG_AT_SEND_2_SCTP             0x04000000
 #define SCTP_LOG_AT_SEND_2_OUTQ             0x08000000
-
+#define SCTP_LOG_TRY_ADVANCE                0x10000000
 
 
 #undef SCTP_PACKED

Modified: head/sys/netinet/sctp_constants.h
==============================================================================
--- head/sys/netinet/sctp_constants.h   Sat Mar 14 11:43:38 2009        
(r189789)
+++ head/sys/netinet/sctp_constants.h   Sat Mar 14 13:42:13 2009        
(r189790)
@@ -229,8 +229,8 @@ __FBSDID("$FreeBSD$");
 #define SCTP_THRESHOLD_CLEAR       120
 #define SCTP_THRESHOLD_INCR        121
 #define SCTP_FLIGHT_LOG_DWN_WP_FWD 122
-
-#define SCTP_LOG_MAX_TYPES 123
+#define SCTP_FWD_TSN_CHECK         123
+#define SCTP_LOG_MAX_TYPES 124
 /*
  * To turn on various logging, you must first enable 'options KTR' and
  * you might want to bump the entires 'options KTR_ENTRIES=80000'.

Modified: head/sys/netinet/sctp_indata.c
==============================================================================
--- head/sys/netinet/sctp_indata.c      Sat Mar 14 11:43:38 2009        
(r189789)
+++ head/sys/netinet/sctp_indata.c      Sat Mar 14 13:42:13 2009        
(r189790)
@@ -3680,7 +3680,7 @@ sctp_strike_gap_ack_chunks(struct sctp_t
                                        if (tp1->data != NULL) {
                                                
(void)sctp_release_pr_sctp_chunk(stcb, tp1,
                                                    (SCTP_RESPONSE_TO_USER_REQ 
| SCTP_NOTIFY_DATAGRAM_SENT),
-                                                   &asoc->sent_queue, 
SCTP_SO_NOT_LOCKED);
+                                                   SCTP_SO_NOT_LOCKED);
                                        }
                                        tp1 = TAILQ_NEXT(tp1, sctp_next);
                                        continue;
@@ -3693,7 +3693,7 @@ sctp_strike_gap_ack_chunks(struct sctp_t
                                        if (tp1->data != NULL) {
                                                
(void)sctp_release_pr_sctp_chunk(stcb, tp1,
                                                    (SCTP_RESPONSE_TO_USER_REQ 
| SCTP_NOTIFY_DATAGRAM_SENT),
-                                                   &asoc->sent_queue, 
SCTP_SO_NOT_LOCKED);
+                                                   SCTP_SO_NOT_LOCKED);
                                        }
                                        tp1 = TAILQ_NEXT(tp1, sctp_next);
                                        continue;
@@ -4078,6 +4078,13 @@ sctp_try_advance_peer_ack_point(struct s
                        /* no chance to advance, out of here */
                        break;
                }
+               if (SCTP_BASE_SYSCTL(sctp_logging_level) & 
SCTP_LOG_TRY_ADVANCE) {
+                       if (tp1->sent == SCTP_FORWARD_TSN_SKIP) {
+                               sctp_misc_ints(SCTP_FWD_TSN_CHECK,
+                                   asoc->advanced_peer_ack_point,
+                                   tp1->rec.data.TSN_seq, 0, 0);
+                       }
+               }
                if (!PR_SCTP_ENABLED(tp1->flags)) {
                        /*
                         * We can't fwd-tsn past any that are reliable aka
@@ -4107,7 +4114,7 @@ sctp_try_advance_peer_ack_point(struct s
                                if (tp1->data) {
                                        (void)sctp_release_pr_sctp_chunk(stcb, 
tp1,
                                            (SCTP_RESPONSE_TO_USER_REQ | 
SCTP_NOTIFY_DATAGRAM_SENT),
-                                           &asoc->sent_queue, 
SCTP_SO_NOT_LOCKED);
+                                           SCTP_SO_NOT_LOCKED);
                                }
                        } else {
                                /*
@@ -4124,8 +4131,16 @@ sctp_try_advance_peer_ack_point(struct s
                 */
                if (tp1->sent == SCTP_FORWARD_TSN_SKIP) {
                        /* advance PeerAckPoint goes forward */
-                       asoc->advanced_peer_ack_point = tp1->rec.data.TSN_seq;
-                       a_adv = tp1;
+                       if (compare_with_wrap(tp1->rec.data.TSN_seq,
+                           asoc->advanced_peer_ack_point,
+                           MAX_TSN)) {
+
+                               asoc->advanced_peer_ack_point = 
tp1->rec.data.TSN_seq;
+                               a_adv = tp1;
+                       } else if (tp1->rec.data.TSN_seq == 
asoc->advanced_peer_ack_point) {
+                               /* No update but we do save the chk */
+                               a_adv = tp1;
+                       }
                } else {
                        /*
                         * If it is still in RESEND we can advance no
@@ -4142,14 +4157,27 @@ sctp_try_advance_peer_ack_point(struct s
        return (a_adv);
 }
 
-static void
+static int
 sctp_fs_audit(struct sctp_association *asoc)
 {
        struct sctp_tmit_chunk *chk;
        int inflight = 0, resend = 0, inbetween = 0, acked = 0, above = 0;
+       int entry_flight, entry_cnt, ret;
+
+       entry_flight = asoc->total_flight;
+       entry_cnt = asoc->total_flight_count;
+       ret = 0;
+
+       if (asoc->pr_sctp_cnt >= asoc->sent_queue_cnt)
+               return (0);
 
        TAILQ_FOREACH(chk, &asoc->sent_queue, sctp_next) {
                if (chk->sent < SCTP_DATAGRAM_RESEND) {
+                       printf("Chk TSN:%u size:%d inflight cnt:%d\n",
+                           chk->rec.data.TSN_seq,
+                           chk->send_size,
+                           chk->snd_count
+                           );
                        inflight++;
                } else if (chk->sent == SCTP_DATAGRAM_RESEND) {
                        resend++;
@@ -4166,10 +4194,15 @@ sctp_fs_audit(struct sctp_association *a
 #ifdef INVARIANTS
                panic("Flight size-express incorrect? \n");
 #else
-               SCTP_PRINTF("Flight size-express incorrect inflight:%d 
inbetween:%d\n",
-                   inflight, inbetween);
+               printf("asoc->total_flight:%d cnt:%d\n",
+                   entry_flight, entry_cnt);
+
+               SCTP_PRINTF("Flight size-express incorrect F:%d I:%d R:%d Ab:%d 
ACK:%d\n",
+                   inflight, inbetween, resend, above, acked);
+               ret = 1;
 #endif
        }
+       return (ret);
 }
 
 
@@ -4590,20 +4623,26 @@ again:
            (asoc->sent_queue_retran_cnt == 0) &&
            (win_probe_recovered == 0) &&
            (done_once == 0)) {
-               /* huh, this should not happen */
-               sctp_fs_audit(asoc);
-               TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
-                       net->flight_size = 0;
-               }
-               asoc->total_flight = 0;
-               asoc->total_flight_count = 0;
-               asoc->sent_queue_retran_cnt = 0;
-               TAILQ_FOREACH(tp1, &asoc->sent_queue, sctp_next) {
-                       if (tp1->sent < SCTP_DATAGRAM_RESEND) {
-                               sctp_flight_size_increase(tp1);
-                               sctp_total_flight_increase(stcb, tp1);
-                       } else if (tp1->sent == SCTP_DATAGRAM_RESEND) {
-                               asoc->sent_queue_retran_cnt++;
+               /*
+                * huh, this should not happen unless all packets are
+                * PR-SCTP and marked to skip of course.
+                */
+               if (sctp_fs_audit(asoc)) {
+                       TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+                               if (net->flight_size) {
+                                       net->flight_size = 0;
+                               }
+                       }
+                       asoc->total_flight = 0;
+                       asoc->total_flight_count = 0;
+                       asoc->sent_queue_retran_cnt = 0;
+                       TAILQ_FOREACH(tp1, &asoc->sent_queue, sctp_next) {
+                               if (tp1->sent < SCTP_DATAGRAM_RESEND) {
+                                       sctp_flight_size_increase(tp1);
+                                       sctp_total_flight_increase(stcb, tp1);
+                               } else if (tp1->sent == SCTP_DATAGRAM_RESEND) {
+                                       asoc->sent_queue_retran_cnt++;
+                               }
                        }
                }
                done_once = 1;
@@ -4728,6 +4767,13 @@ again:
                                 */
                                asoc->nonce_sum_check = 0;
                                asoc->nonce_resync_tsn = 
asoc->advanced_peer_ack_point;
+                       } else if (lchk) {
+                               /* try to FR fwd-tsn's that get lost too */
+                               lchk->rec.data.fwd_tsn_cnt++;
+                               if (lchk->rec.data.fwd_tsn_cnt > 3) {
+                                       send_forward_tsn(stcb, asoc);
+                                       lchk->rec.data.fwd_tsn_cnt = 0;
+                               }
                        }
                }
                if (lchk) {
@@ -4813,10 +4859,6 @@ sctp_handle_sack(struct mbuf *m, int off
        num_seg = ntohs(sack->num_gap_ack_blks);
        a_rwnd = rwnd;
 
-       if (SCTP_BASE_SYSCTL(sctp_logging_level) & 
SCTP_LOG_SACK_ARRIVALS_ENABLE) {
-               sctp_misc_ints(SCTP_SACK_LOG_NORMAL, cum_ack,
-                   rwnd, stcb->asoc.last_acked_seq, stcb->asoc.peers_rwnd);
-       }
        /* CMT DAC algo */
        cmt_dac_flag = ch->ch.chunk_flags & SCTP_SACK_CMT_DAC;
        num_dup = ntohs(sack->num_dup_tsns);
@@ -5605,20 +5647,24 @@ again:
            (asoc->sent_queue_retran_cnt == 0) &&
            (win_probe_recovered == 0) &&
            (done_once == 0)) {
-               /* huh, this should not happen */
-               sctp_fs_audit(asoc);
-               TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
-                       net->flight_size = 0;
-               }
-               asoc->total_flight = 0;
-               asoc->total_flight_count = 0;
-               asoc->sent_queue_retran_cnt = 0;
-               TAILQ_FOREACH(tp1, &asoc->sent_queue, sctp_next) {
-                       if (tp1->sent < SCTP_DATAGRAM_RESEND) {
-                               sctp_flight_size_increase(tp1);
-                               sctp_total_flight_increase(stcb, tp1);
-                       } else if (tp1->sent == SCTP_DATAGRAM_RESEND) {
-                               asoc->sent_queue_retran_cnt++;
+               /*
+                * huh, this should not happen unless all packets are
+                * PR-SCTP and marked to skip of course.
+                */
+               if (sctp_fs_audit(asoc)) {
+                       TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+                               net->flight_size = 0;
+                       }
+                       asoc->total_flight = 0;
+                       asoc->total_flight_count = 0;
+                       asoc->sent_queue_retran_cnt = 0;
+                       TAILQ_FOREACH(tp1, &asoc->sent_queue, sctp_next) {
+                               if (tp1->sent < SCTP_DATAGRAM_RESEND) {
+                                       sctp_flight_size_increase(tp1);
+                                       sctp_total_flight_increase(stcb, tp1);
+                               } else if (tp1->sent == SCTP_DATAGRAM_RESEND) {
+                                       asoc->sent_queue_retran_cnt++;
+                               }
                        }
                }
                done_once = 1;
@@ -5643,6 +5689,11 @@ again:
                         * on issues that will occur when the ECN NONCE
                         * stuff is put into SCTP for cross checking.
                         */
+                       if (SCTP_BASE_SYSCTL(sctp_logging_level) & 
SCTP_LOG_TRY_ADVANCE) {
+                               sctp_misc_ints(SCTP_FWD_TSN_CHECK,
+                                   0xee, cum_ack, 
asoc->advanced_peer_ack_point,
+                                   old_adv_peer_ack_point);
+                       }
                        if (compare_with_wrap(asoc->advanced_peer_ack_point, 
old_adv_peer_ack_point,
                            MAX_TSN)) {
                                send_forward_tsn(stcb, asoc);
@@ -5652,6 +5703,13 @@ again:
                                 */
                                asoc->nonce_sum_check = 0;
                                asoc->nonce_resync_tsn = 
asoc->advanced_peer_ack_point;
+                       } else if (lchk) {
+                               /* try to FR fwd-tsn's that get lost too */
+                               lchk->rec.data.fwd_tsn_cnt++;
+                               if (lchk->rec.data.fwd_tsn_cnt > 3) {
+                                       send_forward_tsn(stcb, asoc);
+                                       lchk->rec.data.fwd_tsn_cnt = 0;
+                               }
                        }
                }
                if (lchk) {
@@ -6019,7 +6077,6 @@ sctp_handle_forward_tsn(struct sctp_tcb 
                        return;
                }
                SCTP_STAT_INCR(sctps_fwdtsn_map_over);
-slide_out:
                memset(stcb->asoc.mapping_array, 0, 
stcb->asoc.mapping_array_size);
                cumack_set_flag = 1;
                asoc->mapping_array_base_tsn = new_cum_tsn + 1;
@@ -6043,13 +6100,8 @@ slide_out:
                asoc->last_echo_tsn = asoc->highest_tsn_inside_map;
        } else {
                SCTP_TCB_LOCK_ASSERT(stcb);
-               if ((compare_with_wrap(((uint32_t) asoc->cumulative_tsn + gap), 
asoc->highest_tsn_inside_map, MAX_TSN)) ||
-                   (((uint32_t) asoc->cumulative_tsn + gap) == 
asoc->highest_tsn_inside_map)) {
-                       goto slide_out;
-               } else {
-                       for (i = 0; i <= gap; i++) {
-                               SCTP_SET_TSN_PRESENT(asoc->mapping_array, i);
-                       }
+               for (i = 0; i <= gap; i++) {
+                       SCTP_SET_TSN_PRESENT(asoc->mapping_array, i);
                }
                /*
                 * Now after marking all, slide thing forward but no sack
@@ -6059,7 +6111,6 @@ slide_out:
                if (*abort_flag)
                        return;
        }
-
        /*************************************************************/
        /* 2. Clear up re-assembly queue                             */
        /*************************************************************/
@@ -6083,9 +6134,9 @@ slide_out:
                chk = TAILQ_FIRST(&asoc->reasmqueue);
                while (chk) {
                        at = TAILQ_NEXT(chk, sctp_next);
-                       if (compare_with_wrap(asoc->cumulative_tsn,
-                           chk->rec.data.TSN_seq, MAX_TSN) ||
-                           asoc->cumulative_tsn == chk->rec.data.TSN_seq) {
+                       if ((compare_with_wrap(new_cum_tsn,
+                           chk->rec.data.TSN_seq, MAX_TSN)) ||
+                           (new_cum_tsn == chk->rec.data.TSN_seq)) {
                                /* It needs to be tossed */
                                TAILQ_REMOVE(&asoc->reasmqueue, chk, sctp_next);
                                if (compare_with_wrap(chk->rec.data.TSN_seq,
@@ -6614,20 +6665,24 @@ again:
            (asoc->sent_queue_retran_cnt == 0) &&
            (win_probe_recovered == 0) &&
            (done_once == 0)) {
-               /* huh, this should not happen */
-               sctp_fs_audit(asoc);
-               TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
-                       net->flight_size = 0;
-               }
-               asoc->total_flight = 0;
-               asoc->total_flight_count = 0;
-               asoc->sent_queue_retran_cnt = 0;
-               TAILQ_FOREACH(tp1, &asoc->sent_queue, sctp_next) {
-                       if (tp1->sent < SCTP_DATAGRAM_RESEND) {
-                               sctp_flight_size_increase(tp1);
-                               sctp_total_flight_increase(stcb, tp1);
-                       } else if (tp1->sent == SCTP_DATAGRAM_RESEND) {
-                               asoc->sent_queue_retran_cnt++;
+               /*
+                * huh, this should not happen unless all packets are
+                * PR-SCTP and marked to skip of course.
+                */
+               if (sctp_fs_audit(asoc)) {
+                       TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+                               net->flight_size = 0;
+                       }
+                       asoc->total_flight = 0;
+                       asoc->total_flight_count = 0;
+                       asoc->sent_queue_retran_cnt = 0;
+                       TAILQ_FOREACH(tp1, &asoc->sent_queue, sctp_next) {
+                               if (tp1->sent < SCTP_DATAGRAM_RESEND) {
+                                       sctp_flight_size_increase(tp1);
+                                       sctp_total_flight_increase(stcb, tp1);
+                               } else if (tp1->sent == SCTP_DATAGRAM_RESEND) {
+                                       asoc->sent_queue_retran_cnt++;
+                               }
                        }
                }
                done_once = 1;
@@ -8170,20 +8225,24 @@ again:
            (asoc->sent_queue_retran_cnt == 0) &&
            (win_probe_recovered == 0) &&
            (done_once == 0)) {
-               /* huh, this should not happen */
-               sctp_fs_audit(asoc);
-               TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
-                       net->flight_size = 0;
-               }
-               asoc->total_flight = 0;
-               asoc->total_flight_count = 0;
-               asoc->sent_queue_retran_cnt = 0;
-               TAILQ_FOREACH(tp1, &asoc->sent_queue, sctp_next) {
-                       if (tp1->sent < SCTP_DATAGRAM_RESEND) {
-                               sctp_flight_size_increase(tp1);
-                               sctp_total_flight_increase(stcb, tp1);
-                       } else if (tp1->sent == SCTP_DATAGRAM_RESEND) {
-                               asoc->sent_queue_retran_cnt++;
+               /*
+                * huh, this should not happen unless all packets are
+                * PR-SCTP and marked to skip of course.
+                */
+               if (sctp_fs_audit(asoc)) {
+                       TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+                               net->flight_size = 0;
+                       }
+                       asoc->total_flight = 0;
+                       asoc->total_flight_count = 0;
+                       asoc->sent_queue_retran_cnt = 0;
+                       TAILQ_FOREACH(tp1, &asoc->sent_queue, sctp_next) {
+                               if (tp1->sent < SCTP_DATAGRAM_RESEND) {
+                                       sctp_flight_size_increase(tp1);
+                                       sctp_total_flight_increase(stcb, tp1);
+                               } else if (tp1->sent == SCTP_DATAGRAM_RESEND) {
+                                       asoc->sent_queue_retran_cnt++;
+                               }
                        }
                }
                done_once = 1;
@@ -8221,6 +8280,13 @@ again:
                                 */
                                asoc->nonce_sum_check = 0;
                                asoc->nonce_resync_tsn = 
asoc->advanced_peer_ack_point;
+                       } else if (lchk) {
+                               /* try to FR fwd-tsn's that get lost too */
+                               lchk->rec.data.fwd_tsn_cnt++;
+                               if (lchk->rec.data.fwd_tsn_cnt > 3) {
+                                       send_forward_tsn(stcb, asoc);
+                                       lchk->rec.data.fwd_tsn_cnt = 0;
+                               }
                        }
                }
                if (lchk) {

Modified: head/sys/netinet/sctp_output.c
==============================================================================
--- head/sys/netinet/sctp_output.c      Sat Mar 14 11:43:38 2009        
(r189789)
+++ head/sys/netinet/sctp_output.c      Sat Mar 14 13:42:13 2009        
(r189790)
@@ -7513,7 +7513,7 @@ sctp_prune_prsctp(struct sctp_tcb *stcb,
                                                        cause = 
SCTP_RESPONSE_TO_USER_REQ | SCTP_NOTIFY_DATAGRAM_UNSENT;
                                                ret_spc = 
sctp_release_pr_sctp_chunk(stcb, chk,
                                                    cause,
-                                                   &asoc->sent_queue, 
SCTP_SO_LOCKED);
+                                                   SCTP_SO_LOCKED);
                                                freed_spc += ret_spc;
                                                if (freed_spc >= dataout) {
                                                        return;
@@ -7538,7 +7538,7 @@ sctp_prune_prsctp(struct sctp_tcb *stcb,
 
                                                ret_spc = 
sctp_release_pr_sctp_chunk(stcb, chk,
                                                    SCTP_RESPONSE_TO_USER_REQ | 
SCTP_NOTIFY_DATAGRAM_UNSENT,
-                                                   &asoc->send_queue, 
SCTP_SO_LOCKED);
+                                                   SCTP_SO_LOCKED);
 
                                                freed_spc += ret_spc;
                                                if (freed_spc >= dataout) {
@@ -8405,6 +8405,7 @@ sctp_clean_up_ctl(struct sctp_tcb *stcb,
                    (chk->rec.chunk_id.id == SCTP_NR_SELECTIVE_ACK) ||  /* EY */
                    (chk->rec.chunk_id.id == SCTP_HEARTBEAT_REQUEST) ||
                    (chk->rec.chunk_id.id == SCTP_HEARTBEAT_ACK) ||
+                   (chk->rec.chunk_id.id == SCTP_FORWARD_CUM_TSN) ||
                    (chk->rec.chunk_id.id == SCTP_SHUTDOWN) ||
                    (chk->rec.chunk_id.id == SCTP_SHUTDOWN_ACK) ||
                    (chk->rec.chunk_id.id == SCTP_OPERATION_ERROR) ||
@@ -8547,7 +8548,7 @@ one_more_time:
                         * when we took all the data the sender_all_done was
                         * not set.
                         */
-                       if (sp->put_last_out == 0) {
+                       if ((sp->put_last_out == 0) && (sp->discard_rest == 0)) 
{
                                SCTP_PRINTF("Gak, put out entire msg with NO 
end!-1\n");
                                SCTP_PRINTF("sender_done:%d len:%d msg_comp:%d 
put_last_out:%d send_lock:%d\n",
                                    sp->sender_all_done,
@@ -8568,7 +8569,6 @@ one_more_time:
                                sp->data = NULL;
                        }
                        sctp_free_a_strmoq(stcb, sp);
-
                        /* we can't be locked to it */
                        *locked = 0;
                        stcb->asoc.locked_on_sending = NULL;
@@ -8596,6 +8596,29 @@ one_more_time:
                        *giveup = 1;
                        to_move = 0;
                        goto out_of;
+               } else if (sp->discard_rest) {
+                       if (send_lock_up == 0) {
+                               SCTP_TCB_SEND_LOCK(stcb);
+                               send_lock_up = 1;
+                       }
+                       /* Whack down the size */
+                       
atomic_subtract_int(&stcb->asoc.total_output_queue_size, sp->length);
+                       if ((stcb->sctp_socket != NULL) && \
+                           ((stcb->sctp_ep->sctp_flags & 
SCTP_PCB_FLAGS_TCPTYPE) ||
+                           (stcb->sctp_ep->sctp_flags & 
SCTP_PCB_FLAGS_IN_TCPPOOL))) {
+                               
atomic_subtract_int(&stcb->sctp_socket->so_snd.sb_cc, sp->length);
+                       }
+                       if (sp->data) {
+                               sctp_m_freem(sp->data);
+                               sp->data = NULL;
+                               sp->tail_mbuf = NULL;
+                       }
+                       sp->length = 0;
+                       sp->some_taken = 1;
+                       *locked = 1;
+                       *giveup = 1;
+                       to_move = 0;
+                       goto out_of;
                }
        }
        some_taken = sp->some_taken;
@@ -11533,6 +11556,7 @@ send_forward_tsn(struct sctp_tcb *stcb,
 {
        struct sctp_tmit_chunk *chk;
        struct sctp_forward_tsn_chunk *fwdtsn;
+       uint32_t advance_peer_ack_point;
 
        SCTP_TCB_LOCK_ASSERT(stcb);
        TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) {
@@ -11610,11 +11634,23 @@ sctp_fill_in_rest:
                        /* 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, 0xff, cnt_of_space,
+                                   space_needed);
+                       }
                        cnt_of_skipped = (cnt_of_space -
                            ((sizeof(struct sctp_forward_tsn_chunk)) /
                            sizeof(struct sctp_strseq)));
@@ -11627,12 +11663,17 @@ sctp_fill_in_rest:
                                tp1 = TAILQ_NEXT(at, sctp_next);
                                at = tp1;
                        }
+                       if (SCTP_BASE_SYSCTL(sctp_logging_level) & 
SCTP_LOG_TRY_ADVANCE) {
+                               sctp_misc_ints(SCTP_FWD_TSN_CHECK,
+                                   0xff, cnt_of_skipped, at->rec.data.TSN_seq,
+                                   asoc->advanced_peer_ack_point);
+                       }
                        last = at;
                        /*-
                         * last now points to last one I can report, update
                         * peer ack point
                         */
-                       asoc->advanced_peer_ack_point = last->rec.data.TSN_seq;
+                       advance_peer_ack_point = last->rec.data.TSN_seq;
                        space_needed -= (cnt_of_skipped * sizeof(struct 
sctp_strseq));
                }
                chk->send_size = space_needed;
@@ -11641,7 +11682,7 @@ sctp_fill_in_rest:
                fwdtsn->ch.chunk_length = htons(chk->send_size);
                fwdtsn->ch.chunk_flags = 0;
                fwdtsn->ch.chunk_type = SCTP_FORWARD_CUM_TSN;
-               fwdtsn->new_cumulative_tsn = 
htonl(asoc->advanced_peer_ack_point);
+               fwdtsn->new_cumulative_tsn = htonl(advance_peer_ack_point);
                chk->send_size = (sizeof(struct sctp_forward_tsn_chunk) +
                    (cnt_of_skipped * sizeof(struct sctp_strseq)));
                SCTP_BUF_LEN(chk->data) = chk->send_size;
@@ -11672,6 +11713,9 @@ sctp_fill_in_rest:
                                at = tp1;
                                continue;
                        }
+                       if (at->rec.data.TSN_seq == advance_peer_ack_point) {
+                               at->rec.data.fwd_tsn_cnt = 0;
+                       }
                        strseq->stream = ntohs(at->rec.data.stream_number);
                        strseq->sequence = ntohs(at->rec.data.stream_seq);
                        strseq++;

Modified: head/sys/netinet/sctp_timer.c
==============================================================================
--- head/sys/netinet/sctp_timer.c       Sat Mar 14 11:43:38 2009        
(r189789)
+++ head/sys/netinet/sctp_timer.c       Sat Mar 14 13:42:13 2009        
(r189790)
@@ -767,7 +767,7 @@ start_again:
                                                
(void)sctp_release_pr_sctp_chunk(stcb,
                                                    chk,
                                                    (SCTP_RESPONSE_TO_USER_REQ 
| SCTP_NOTIFY_DATAGRAM_SENT),
-                                                   &stcb->asoc.sent_queue, 
SCTP_SO_NOT_LOCKED);
+                                                   SCTP_SO_NOT_LOCKED);
                                        }
                                        continue;
                                }
@@ -779,7 +779,7 @@ start_again:
                                                
(void)sctp_release_pr_sctp_chunk(stcb,
                                                    chk,
                                                    (SCTP_RESPONSE_TO_USER_REQ 
| SCTP_NOTIFY_DATAGRAM_SENT),
-                                                   &stcb->asoc.sent_queue, 
SCTP_SO_NOT_LOCKED);
+                                                   SCTP_SO_NOT_LOCKED);
                                        }
                                        continue;
                                }

Modified: head/sys/netinet/sctp_var.h
==============================================================================
--- head/sys/netinet/sctp_var.h Sat Mar 14 11:43:38 2009        (r189789)
+++ head/sys/netinet/sctp_var.h Sat Mar 14 13:42:13 2009        (r189790)
@@ -96,7 +96,8 @@ extern struct pr_usrreqs sctp_usrreqs;
 
 #define sctp_alloc_a_strmoq(_stcb, _strmoq) { \
        (_strmoq) = SCTP_ZONE_GET(SCTP_BASE_INFO(ipi_zone_strmoq), struct 
sctp_stream_queue_pending); \
-       if ((_strmoq)) { \
+         if ((_strmoq)) {                        \
+               memset(_strmoq, 0, sizeof(struct sctp_stream_queue_pending)); \
                SCTP_INCR_STRMOQ_COUNT(); \
                (_strmoq)->holds_key_ref = 0; \
        } \
@@ -267,6 +268,7 @@ extern struct pr_usrreqs sctp_usrreqs;
 #else
 
 #define sctp_total_flight_decrease(stcb, tp1) do { \
+        tp1->window_probe = 0; \
        if (stcb->asoc.total_flight >= tp1->book_size) { \
                stcb->asoc.total_flight -= tp1->book_size; \
                if (stcb->asoc.total_flight_count > 0) \

Modified: head/sys/netinet/sctputil.c
==============================================================================
--- head/sys/netinet/sctputil.c Sat Mar 14 11:43:38 2009        (r189789)
+++ head/sys/netinet/sctputil.c Sat Mar 14 13:42:13 2009        (r189790)
@@ -3686,10 +3686,10 @@ sctp_report_all_outbound(struct sctp_tcb
                while (chk) {
                        TAILQ_REMOVE(&asoc->sent_queue, chk, sctp_next);
                        asoc->sent_queue_cnt--;
-                       sctp_free_bufspace(stcb, asoc, chk, 1);
-                       sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb,
-                           SCTP_NOTIFY_DATAGRAM_SENT, chk, so_locked);
-                       if (chk->data) {
+                       if (chk->data != NULL) {
+                               sctp_free_bufspace(stcb, asoc, chk, 1);
+                               sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb,
+                                   SCTP_NOTIFY_DATAGRAM_SENT, chk, so_locked);
                                sctp_m_freem(chk->data);
                                chk->data = NULL;
                        }
@@ -3704,9 +3704,10 @@ sctp_report_all_outbound(struct sctp_tcb
                while (chk) {
                        TAILQ_REMOVE(&asoc->send_queue, chk, sctp_next);
                        asoc->send_queue_cnt--;
-                       sctp_free_bufspace(stcb, asoc, chk, 1);
-                       sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, 
SCTP_NOTIFY_DATAGRAM_UNSENT, chk, so_locked);
-                       if (chk->data) {
+                       if (chk->data != NULL) {
+                               sctp_free_bufspace(stcb, asoc, chk, 1);
+                               sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb,
+                                   SCTP_NOTIFY_DATAGRAM_UNSENT, chk, 
so_locked);
                                sctp_m_freem(chk->data);
                                chk->data = NULL;
                        }
@@ -4630,64 +4631,46 @@ sctp_free_bufspace(struct sctp_tcb *stcb
 
 int
 sctp_release_pr_sctp_chunk(struct sctp_tcb *stcb, struct sctp_tmit_chunk *tp1,
-    int reason, struct sctpchunk_listhead *queue, int so_locked
+    int reason, int so_locked
 #if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING)
     SCTP_UNUSED
 #endif
 )
 {
+       struct sctp_stream_out *strq;
+       struct sctp_tmit_chunk *chk = NULL;
+       struct sctp_stream_queue_pending *sp;
+       uint16_t stream = 0, seq = 0;
+       uint8_t foundeom = 0;
        int ret_sz = 0;
        int notdone;
-       uint8_t foundeom = 0;
+       int do_wakeup_routine = 0;
 
+       stream = tp1->rec.data.stream_number;
+       seq = tp1->rec.data.stream_seq;
        do {
                ret_sz += tp1->book_size;
                tp1->sent = SCTP_FORWARD_TSN_SKIP;
-               if (tp1->data) {
+               if (tp1->data != NULL) {
 #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
                        struct socket *so;
 
 #endif
+                       printf("Release PR-SCTP chunk tsn:%u flags:%x\n",
+                           tp1->rec.data.TSN_seq,
+                           (unsigned int)tp1->rec.data.rcv_flags);
                        sctp_free_bufspace(stcb, &stcb->asoc, tp1, 1);
                        sctp_flight_size_decrease(tp1);
                        sctp_total_flight_decrease(stcb, tp1);
+                       stcb->asoc.peers_rwnd += tp1->send_size;
+                       stcb->asoc.peers_rwnd += 
SCTP_BASE_SYSCTL(sctp_peer_chunk_oh);
                        sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, reason, tp1, 
so_locked);
                        sctp_m_freem(tp1->data);
                        tp1->data = NULL;
-#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
-                       so = SCTP_INP_SO(stcb->sctp_ep);
-                       if (!so_locked) {
-                               atomic_add_int(&stcb->asoc.refcnt, 1);
-                               SCTP_TCB_UNLOCK(stcb);
-                               SCTP_SOCKET_LOCK(so, 1);
-                               SCTP_TCB_LOCK(stcb);
-                               atomic_subtract_int(&stcb->asoc.refcnt, 1);
-                               if (stcb->asoc.state & 
SCTP_STATE_CLOSED_SOCKET) {
-                                       /*
-                                        * assoc was freed while we were
-                                        * unlocked
-                                        */
-                                       SCTP_SOCKET_UNLOCK(so, 1);
-                                       return (ret_sz);
-                               }
-                       }
-#endif
-                       sctp_sowwakeup(stcb->sctp_ep, stcb->sctp_socket);
-#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
-                       if (!so_locked) {
-                               SCTP_SOCKET_UNLOCK(so, 1);
+                       do_wakeup_routine = 1;
+                       if (PR_SCTP_BUF_ENABLED(tp1->flags)) {
+                               stcb->asoc.sent_queue_cnt_removeable--;
                        }
-#endif
-               }
-               if (PR_SCTP_BUF_ENABLED(tp1->flags)) {
-                       stcb->asoc.sent_queue_cnt_removeable--;
-               }
-               if (queue == &stcb->asoc.send_queue) {
-                       TAILQ_REMOVE(&stcb->asoc.send_queue, tp1, sctp_next);
-                       /* on to the sent queue */
-                       TAILQ_INSERT_TAIL(&stcb->asoc.sent_queue, tp1,
-                           sctp_next);
-                       stcb->asoc.sent_queue_cnt++;
                }
                if ((tp1->rec.data.rcv_flags & SCTP_DATA_NOT_FRAG) ==
                    SCTP_DATA_NOT_FRAG) {
@@ -4707,22 +4690,146 @@ sctp_release_pr_sctp_chunk(struct sctp_t
                        tp1 = TAILQ_NEXT(tp1, sctp_next);
                }
        } while (tp1 && notdone);
-       if ((foundeom == 0) && (queue == &stcb->asoc.sent_queue)) {
+       if (foundeom == 0) {
                /*
                 * The multi-part message was scattered across the send and
                 * sent queue.
                 */
+next_on_sent:
                tp1 = TAILQ_FIRST(&stcb->asoc.send_queue);
                /*
                 * recurse throught the send_queue too, starting at the
                 * beginning.
                 */
-               if (tp1) {
-                       ret_sz += sctp_release_pr_sctp_chunk(stcb, tp1, reason,
-                           &stcb->asoc.send_queue, so_locked);
-               } else {
-                       SCTP_PRINTF("hmm, nothing on the send queue and no 
EOM?\n");
+               if ((tp1) &&
+                   (tp1->rec.data.stream_number == stream) &&
+                   (tp1->rec.data.stream_seq == seq)
+                   ) {
+                       /*
+                        * save to chk in case we have some on stream out
+                        * queue. If so and we have an un-transmitted one we
+                        * don't have to fudge the TSN.
+                        */
+                       chk = tp1;
+                       ret_sz += tp1->book_size;
+                       sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, reason, tp1, 
so_locked);
+                       sctp_free_bufspace(stcb, &stcb->asoc, tp1, 1);
+                       sctp_m_freem(tp1->data);
+                       tp1->data = NULL;
+                       if (tp1->rec.data.rcv_flags & SCTP_DATA_LAST_FRAG) {
+                               foundeom = 1;
+                       }
+                       do_wakeup_routine = 1;
+                       tp1->sent = SCTP_FORWARD_TSN_SKIP;
+                       TAILQ_REMOVE(&stcb->asoc.send_queue, tp1, sctp_next);
+                       /*
+                        * on to the sent queue so we can wait for it to be
+                        * passed by.
+                        */
+                       TAILQ_INSERT_TAIL(&stcb->asoc.sent_queue, tp1,
+                           sctp_next);
+                       stcb->asoc.send_queue_cnt--;
+                       stcb->asoc.sent_queue_cnt++;
+                       goto next_on_sent;
+               }
+       }
+       if (foundeom == 0) {
+               /*
+                * Still no eom found. That means there is stuff left on the
+                * stream out queue.. yuck.
+                */
+               strq = &stcb->asoc.strmout[stream];
+               SCTP_TCB_SEND_LOCK(stcb);
+               sp = TAILQ_FIRST(&strq->outqueue);
+               while (sp->strseq <= seq) {
+                       /* Check if its our SEQ */
+                       if (sp->strseq == seq) {
+                               sp->discard_rest = 1;
+                               /*
+                                * We may need to put a chunk on the queue
+                                * that holds the TSN that would have been
+                                * sent with the LAST bit.
+                                */
+                               if (chk == NULL) {
+                                       /* Yep, we have to */
+                                       sctp_alloc_a_chunk(stcb, chk);
+                                       if (chk == NULL) {
+                                               /*
+                                                * we are hosed. All we can
+                                                * do is nothing.. which
+                                                * will cause an abort if
+                                                * the peer is paying
+                                                * attention.
+                                                */
+                                               goto oh_well;
+                                       }
+                                       memset(chk, 0, sizeof(*chk));
+                                       chk->rec.data.rcv_flags = 
SCTP_DATA_LAST_FRAG;
+                                       chk->sent = SCTP_FORWARD_TSN_SKIP;
+                                       chk->asoc = &stcb->asoc;
+                                       chk->rec.data.stream_seq = sp->strseq;
+                                       chk->rec.data.stream_number = 
sp->stream;
+                                       chk->rec.data.payloadtype = sp->ppid;
+                                       chk->rec.data.context = sp->context;
+                                       chk->flags = sp->act_flags;
+                                       chk->addr_over = sp->addr_over;
+                                       chk->whoTo = sp->net;
+                                       atomic_add_int(&chk->whoTo->ref_count, 
1);
+                                       chk->rec.data.TSN_seq = 
atomic_fetchadd_int(&stcb->asoc.sending_seq, 1);
+                                       stcb->asoc.pr_sctp_cnt++;
+                                       chk->pr_sctp_on = 1;
+                                       
TAILQ_INSERT_TAIL(&stcb->asoc.sent_queue, chk, sctp_next);
+                                       stcb->asoc.sent_queue_cnt++;
+                               } else {
+                                       chk->rec.data.rcv_flags |= 
SCTP_DATA_LAST_FRAG;
+                               }
+               oh_well:
+                               if (sp->data) {
+                                       /*
+                                        * Pull any data to free up the SB
+                                        * and allow sender to "add more"
+                                        * whilc we will throw away :-)
+                                        */
+                                       sctp_free_spbufspace(stcb, &stcb->asoc,
+                                           sp);
+                                       ret_sz += sp->length;
+                                       do_wakeup_routine = 1;
+                                       sp->some_taken = 1;
+                                       sctp_m_freem(sp->data);
+                                       sp->length = 0;
+                                       sp->data = NULL;
+                                       sp->tail_mbuf = NULL;
+                               }
+                               break;
+                       } else {
+                               /* Next one please */
+                               sp = TAILQ_NEXT(sp, next);
+                       }
+               }               /* End while */
+               SCTP_TCB_SEND_UNLOCK(stcb);
+       }
+       if (do_wakeup_routine) {
+#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
+               so = SCTP_INP_SO(stcb->sctp_ep);
+               if (!so_locked) {
+                       atomic_add_int(&stcb->asoc.refcnt, 1);
+                       SCTP_TCB_UNLOCK(stcb);
+                       SCTP_SOCKET_LOCK(so, 1);
+                       SCTP_TCB_LOCK(stcb);
+                       atomic_subtract_int(&stcb->asoc.refcnt, 1);
+                       if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) {
+                               /* assoc was freed while we were unlocked */
+                               SCTP_SOCKET_UNLOCK(so, 1);
+                               return (ret_sz);
+                       }
                }
+#endif
+               sctp_sowwakeup(stcb->sctp_ep, stcb->sctp_socket);
+#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
+               if (!so_locked) {
+                       SCTP_SOCKET_UNLOCK(so, 1);
+               }
+#endif
        }
        return (ret_sz);
 }

Modified: head/sys/netinet/sctputil.h
==============================================================================
--- head/sys/netinet/sctputil.h Sat Mar 14 11:43:38 2009        (r189789)
+++ head/sys/netinet/sctputil.h Sat Mar 14 13:42:13 2009        (r189790)
@@ -237,7 +237,7 @@ sctp_notify_partial_delivery_indication(
 
 int
 sctp_release_pr_sctp_chunk(struct sctp_tcb *, struct sctp_tmit_chunk *,
-    int, struct sctpchunk_listhead *, int
+    int, int
 #if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING)
     SCTP_UNUSED
 #endif
@@ -287,7 +287,6 @@ do { \
 #define sctp_free_spbufspace(stcb, asoc, sp)  \
 do { \
        if (sp->data != NULL) { \
-                atomic_subtract_int(&(asoc)->chunks_on_out_queue, 1); \
                if ((asoc)->total_output_queue_size >= sp->length) { \
                        atomic_subtract_int(&(asoc)->total_output_queue_size, 
sp->length); \
                } else { \
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to