+
+/*
+ * Send as many acks as data coalesced. Every other packet when stretch
+ * ACK is not enabled. Every 8 packets, if stretch ACK is enabled.
+ */
+static struct mbuf*
+tcp_send_lroacks(struct tcpcb *tp, struct mbuf *m, struct tcphdr *th)
+{
+ struct mbuf *mnext = NULL, *ack_chain = NULL, *tail = NULL;
+ int count = 0;
+ tcp_seq org_ack = ntohl(th->th_ack);
+ tcp_seq prev_ack = 0;
+ int tack_offset = 28; /* XXX IPv6 and IP options not supported */
+ int twin_offset = 34; /* XXX IPv6 and IP options not supported */
+ int ack_size = (tp->t_flags & TF_STRETCHACK) ?
+ (maxseg_unacked * tp->t_maxseg) : (tp->t_maxseg << 1);
+ int segs_acked = (tp->t_flags & TF_STRETCHACK) ? maxseg_unacked : 2;
+ struct mbuf *prev_ack_pkt = NULL;
+ struct socket *so = tp->t_inpcb->inp_socket;
+ unsigned short winsz = ntohs(th->th_win);
+ unsigned int scaled_win = winsz<<tp->rcv_scale;
+ tcp_seq win_rtedge = org_ack + scaled_win;
+
+ count = tp->t_lropktlen/tp->t_maxseg;
+
+ prev_ack = (org_ack - tp->t_lropktlen) + ack_size;
+ if (prev_ack < org_ack) {
+ ack_chain = m_dup(m, M_DONTWAIT);
+ if (ack_chain) {
+ th->th_ack = htonl(prev_ack);
+ /* Keep adv window constant for duplicated ACK packets */
+ scaled_win = win_rtedge - prev_ack;
+ if (scaled_win > (int32_t)(TCP_MAXWIN << tp->rcv_scale))
+ scaled_win = (int32_t)(TCP_MAXWIN << tp->rcv_scale);
+ th->th_win = htons(scaled_win>>tp->rcv_scale);
+ if (lrodebug == 5) {
+ printf("%s: win = %d winsz = %d sc = %d"
+ " lro_len %d %d\n",
+ __func__, scaled_win>>tp->rcv_scale, winsz,
+ tp->rcv_scale, tp->t_lropktlen, count);
+ }
+ tail = ack_chain;
+ count -= segs_acked; /* accounts for prev_ack packet */
+ count = (count <= segs_acked) ? 0 : count - segs_acked;
+ tcpstat.tcps_sndacks++;
+ so_tc_update_stats(m, so, m_get_service_class(m));
+ } else {
+ return NULL;
+ }
+ }
+ else {
+ tp->t_lropktlen = 0;
+ return NULL;
+ }
+
+ prev_ack_pkt = ack_chain;
+
+ while (count > 0) {
+ if ((prev_ack + ack_size) < org_ack) {
+ prev_ack += ack_size;
+ } else {
+ /*
+ * The last ACK sent must have the ACK number that TCP
+ * thinks is the last sent ACK number.
+ */
+ prev_ack = org_ack;
+ }
+ mnext = m_dup(prev_ack_pkt, M_DONTWAIT);
+ if (mnext) {
+ /* Keep adv window constant for duplicated ACK packets */
+ scaled_win = win_rtedge - prev_ack;
+ if (scaled_win > (int32_t)(TCP_MAXWIN << tp->rcv_scale))
+ scaled_win = (int32_t)(TCP_MAXWIN << tp->rcv_scale);
+ winsz = htons(scaled_win>>tp->rcv_scale);
+ if (lrodebug == 5) {
+ printf("%s: winsz = %d ack %x count %d\n",
+ __func__, scaled_win>>tp->rcv_scale,
+ prev_ack, count);
+ }
+ bcopy(&winsz, mtod(prev_ack_pkt, caddr_t) + twin_offset, 2);
+ HTONL(prev_ack);
+ bcopy(&prev_ack, mtod(prev_ack_pkt, caddr_t) + tack_offset, 4);
+ NTOHL(prev_ack);
+ tail->m_nextpkt = mnext;
+ tail = mnext;
+ count -= segs_acked;
+ tcpstat.tcps_sndacks++;
+ so_tc_update_stats(m, so, m_get_service_class(m));
+ } else {
+ if (lrodebug == 5) {
+ printf("%s: failed to alloc mbuf.\n", __func__);
+ }
+ break;
+ }
+ prev_ack_pkt = mnext;
+ }
+ tp->t_lropktlen = 0;
+ return ack_chain;
+}
+
+static int
+tcp_recv_throttle (struct tcpcb *tp)
+{
+ uint32_t base_rtt, newsize;
+ int32_t qdelay;
+ struct sockbuf *sbrcv = &tp->t_inpcb->inp_socket->so_rcv;
+
+ if (tcp_use_rtt_recvbg == 1 &&
+ TSTMP_SUPPORTED(tp)) {
+ /*
+ * Timestamps are supported on this connection. Use
+ * RTT to look for an increase in latency.
+ */
+
+ /*
+ * If the connection is already being throttled, leave it
+ * in that state until rtt comes closer to base rtt
+ */
+ if (tp->t_flagsext & TF_RECV_THROTTLE)
+ return (1);
+
+ base_rtt = get_base_rtt(tp);
+
+ if (base_rtt != 0 && tp->t_rttcur != 0) {
+ qdelay = tp->t_rttcur - base_rtt;
+ /*
+ * if latency increased on a background flow,
+ * return 1 to start throttling.
+ */
+ if (qdelay > target_qdelay) {
+ tp->t_flagsext |= TF_RECV_THROTTLE;
+
+ /*
+ * Reduce the recv socket buffer size to
+ * minimize latecy.
+ */
+ if (sbrcv->sb_idealsize >
+ tcp_recv_throttle_minwin) {
+ newsize = sbrcv->sb_idealsize >> 1;
+ /* Set a minimum of 16 K */
+ newsize =
+ max(newsize,
+ tcp_recv_throttle_minwin);
+ sbrcv->sb_idealsize = newsize;
+ }
+ return (1);
+ } else {
+ return (0);
+ }
+ }
+ }
+
+ /*
+ * Timestamps are not supported or there is no good RTT
+ * measurement. Use IPDV in this case.
+ */
+ if (tp->acc_iaj > tcp_acc_iaj_react_limit)
+ return (1);
+
+ return (0);
+}