14f9c93f1SGleb Smirnoff /*- 24f9c93f1SGleb Smirnoff * Copyright (c) 2016-2018 Netflix, Inc. 34f9c93f1SGleb Smirnoff * Copyright (c) 2016-2021 Mellanox Technologies. 44f9c93f1SGleb Smirnoff * 54f9c93f1SGleb Smirnoff * Redistribution and use in source and binary forms, with or without 64f9c93f1SGleb Smirnoff * modification, are permitted provided that the following conditions 74f9c93f1SGleb Smirnoff * are met: 84f9c93f1SGleb Smirnoff * 1. Redistributions of source code must retain the above copyright 94f9c93f1SGleb Smirnoff * notice, this list of conditions and the following disclaimer. 104f9c93f1SGleb Smirnoff * 2. Redistributions in binary form must reproduce the above copyright 114f9c93f1SGleb Smirnoff * notice, this list of conditions and the following disclaimer in the 124f9c93f1SGleb Smirnoff * documentation and/or other materials provided with the distribution. 134f9c93f1SGleb Smirnoff * 144f9c93f1SGleb Smirnoff * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 154f9c93f1SGleb Smirnoff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 164f9c93f1SGleb Smirnoff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 174f9c93f1SGleb Smirnoff * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 184f9c93f1SGleb Smirnoff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 194f9c93f1SGleb Smirnoff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 204f9c93f1SGleb Smirnoff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 214f9c93f1SGleb Smirnoff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 224f9c93f1SGleb Smirnoff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 234f9c93f1SGleb Smirnoff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 244f9c93f1SGleb Smirnoff * SUCH DAMAGE. 254f9c93f1SGleb Smirnoff * 264f9c93f1SGleb Smirnoff */ 274f9c93f1SGleb Smirnoff #include <sys/cdefs.h> 284f9c93f1SGleb Smirnoff #include "opt_inet.h" 294f9c93f1SGleb Smirnoff #include "opt_inet6.h" 304f9c93f1SGleb Smirnoff 314f9c93f1SGleb Smirnoff #include <sys/param.h> 324f9c93f1SGleb Smirnoff #include <sys/systm.h> 334f9c93f1SGleb Smirnoff #include <sys/kernel.h> 344f9c93f1SGleb Smirnoff #include <sys/malloc.h> 354f9c93f1SGleb Smirnoff #include <sys/mbuf.h> 364f9c93f1SGleb Smirnoff #include <sys/socket.h> 374f9c93f1SGleb Smirnoff #include <sys/socketvar.h> 384f9c93f1SGleb Smirnoff #include <sys/sysctl.h> 394f9c93f1SGleb Smirnoff 404f9c93f1SGleb Smirnoff #include <net/if.h> 414f9c93f1SGleb Smirnoff #include <net/if_var.h> 424d0c9538SJustin Hibbits #include <net/if_private.h> 434f9c93f1SGleb Smirnoff #include <net/ethernet.h> 444f9c93f1SGleb Smirnoff #include <net/bpf.h> 454f9c93f1SGleb Smirnoff #include <net/vnet.h> 464f9c93f1SGleb Smirnoff #include <net/if_dl.h> 474f9c93f1SGleb Smirnoff #include <net/if_media.h> 484f9c93f1SGleb Smirnoff #include <net/if_types.h> 494f9c93f1SGleb Smirnoff #include <net/infiniband.h> 504f9c93f1SGleb Smirnoff #include <net/if_lagg.h> 51d1ce0121SMichael Tuexen #include <net/pfil.h> 524f9c93f1SGleb Smirnoff 534f9c93f1SGleb Smirnoff #include <netinet/in.h> 5496ad6401SMichael Tuexen #include <netinet/in_kdtrace.h> 554f9c93f1SGleb Smirnoff #include <netinet/ip6.h> 564f9c93f1SGleb Smirnoff #include <netinet/ip.h> 574f9c93f1SGleb Smirnoff #include <netinet/ip_var.h> 584f9c93f1SGleb Smirnoff #include <netinet/in_pcb.h> 594f9c93f1SGleb Smirnoff #include <netinet6/in6_pcb.h> 60d1ce0121SMichael Tuexen #include <netinet6/ip6_var.h> 614f9c93f1SGleb Smirnoff #include <netinet/tcp.h> 624f9c93f1SGleb Smirnoff #include <netinet/tcp_lro.h> 634f9c93f1SGleb Smirnoff #include <netinet/tcp_var.h> 644f9c93f1SGleb Smirnoff #include <netinet/tcp_hpts.h> 65*8caa2f53SMichael Tuexen #ifdef TCP_BLACKBOX 664f9c93f1SGleb Smirnoff #include <netinet/tcp_log_buf.h> 67*8caa2f53SMichael Tuexen #endif 684f9c93f1SGleb Smirnoff 694f9c93f1SGleb Smirnoff static void 704f9c93f1SGleb Smirnoff build_ack_entry(struct tcp_ackent *ae, struct tcphdr *th, struct mbuf *m, 714f9c93f1SGleb Smirnoff uint32_t *ts_ptr, uint16_t iptos) 724f9c93f1SGleb Smirnoff { 734f9c93f1SGleb Smirnoff /* 744f9c93f1SGleb Smirnoff * Given a TCP ACK, summarize it down into the small TCP ACK 754f9c93f1SGleb Smirnoff * entry. 764f9c93f1SGleb Smirnoff */ 774f9c93f1SGleb Smirnoff ae->timestamp = m->m_pkthdr.rcv_tstmp; 784f9c93f1SGleb Smirnoff ae->flags = 0; 794f9c93f1SGleb Smirnoff if (m->m_flags & M_TSTMP_LRO) 804f9c93f1SGleb Smirnoff ae->flags |= TSTMP_LRO; 814f9c93f1SGleb Smirnoff else if (m->m_flags & M_TSTMP) 824f9c93f1SGleb Smirnoff ae->flags |= TSTMP_HDWR; 83f30c7d56SMichael Tuexen ae->seq = th->th_seq; 84f30c7d56SMichael Tuexen ae->ack = th->th_ack; 854f9c93f1SGleb Smirnoff ae->flags |= tcp_get_flags(th); 864f9c93f1SGleb Smirnoff if (ts_ptr != NULL) { 874f9c93f1SGleb Smirnoff ae->ts_value = ntohl(ts_ptr[1]); 884f9c93f1SGleb Smirnoff ae->ts_echo = ntohl(ts_ptr[2]); 894f9c93f1SGleb Smirnoff ae->flags |= HAS_TSTMP; 904f9c93f1SGleb Smirnoff } 91f30c7d56SMichael Tuexen ae->win = th->th_win; 924f9c93f1SGleb Smirnoff ae->codepoint = iptos; 934f9c93f1SGleb Smirnoff } 944f9c93f1SGleb Smirnoff 954f9c93f1SGleb Smirnoff static inline bool 964f9c93f1SGleb Smirnoff tcp_lro_ack_valid(struct mbuf *m, struct tcphdr *th, uint32_t **ppts, bool *other_opts) 974f9c93f1SGleb Smirnoff { 984f9c93f1SGleb Smirnoff /* 994f9c93f1SGleb Smirnoff * This function returns two bits of valuable information. 1004f9c93f1SGleb Smirnoff * a) Is what is present capable of being ack-compressed, 1014f9c93f1SGleb Smirnoff * we can ack-compress if there is no options or just 1024f9c93f1SGleb Smirnoff * a timestamp option, and of course the th_flags must 1034f9c93f1SGleb Smirnoff * be correct as well. 1044f9c93f1SGleb Smirnoff * b) Our other options present such as SACK. This is 1054f9c93f1SGleb Smirnoff * used to determine if we want to wakeup or not. 1064f9c93f1SGleb Smirnoff */ 1074f9c93f1SGleb Smirnoff bool ret = true; 1084f9c93f1SGleb Smirnoff 1094f9c93f1SGleb Smirnoff switch (th->th_off << 2) { 1104f9c93f1SGleb Smirnoff case (sizeof(*th) + TCPOLEN_TSTAMP_APPA): 1114f9c93f1SGleb Smirnoff *ppts = (uint32_t *)(th + 1); 1124f9c93f1SGleb Smirnoff /* Check if we have only one timestamp option. */ 1134f9c93f1SGleb Smirnoff if (**ppts == TCP_LRO_TS_OPTION) 1144f9c93f1SGleb Smirnoff *other_opts = false; 1154f9c93f1SGleb Smirnoff else { 1164f9c93f1SGleb Smirnoff *other_opts = true; 1174f9c93f1SGleb Smirnoff ret = false; 1184f9c93f1SGleb Smirnoff } 1194f9c93f1SGleb Smirnoff break; 1204f9c93f1SGleb Smirnoff case (sizeof(*th)): 1214f9c93f1SGleb Smirnoff /* No options. */ 1224f9c93f1SGleb Smirnoff *ppts = NULL; 1234f9c93f1SGleb Smirnoff *other_opts = false; 1244f9c93f1SGleb Smirnoff break; 1254f9c93f1SGleb Smirnoff default: 1264f9c93f1SGleb Smirnoff *ppts = NULL; 1274f9c93f1SGleb Smirnoff *other_opts = true; 1284f9c93f1SGleb Smirnoff ret = false; 1294f9c93f1SGleb Smirnoff break; 1304f9c93f1SGleb Smirnoff } 1314f9c93f1SGleb Smirnoff /* For ACKCMP we only accept ACK, PUSH, ECE and CWR. */ 1324f9c93f1SGleb Smirnoff if ((tcp_get_flags(th) & ~(TH_ACK | TH_PUSH | TH_ECE | TH_CWR)) != 0) 1334f9c93f1SGleb Smirnoff ret = false; 1344f9c93f1SGleb Smirnoff /* If it has data on it we cannot compress it */ 1354f9c93f1SGleb Smirnoff if (m->m_pkthdr.lro_tcp_d_len) 1364f9c93f1SGleb Smirnoff ret = false; 1374f9c93f1SGleb Smirnoff 1384f9c93f1SGleb Smirnoff /* ACK flag must be set. */ 1394f9c93f1SGleb Smirnoff if (!(tcp_get_flags(th) & TH_ACK)) 1404f9c93f1SGleb Smirnoff ret = false; 1414f9c93f1SGleb Smirnoff return (ret); 1424f9c93f1SGleb Smirnoff } 1434f9c93f1SGleb Smirnoff 1444f9c93f1SGleb Smirnoff static bool 1454f9c93f1SGleb Smirnoff tcp_lro_check_wake_status(struct tcpcb *tp) 1464f9c93f1SGleb Smirnoff { 1474f9c93f1SGleb Smirnoff 1484f9c93f1SGleb Smirnoff if (tp->t_fb->tfb_early_wake_check != NULL) 1494f9c93f1SGleb Smirnoff return ((tp->t_fb->tfb_early_wake_check)(tp)); 1504f9c93f1SGleb Smirnoff return (false); 1514f9c93f1SGleb Smirnoff } 1524f9c93f1SGleb Smirnoff 153*8caa2f53SMichael Tuexen #ifdef TCP_BLACKBOX 1544f9c93f1SGleb Smirnoff static void 1554f9c93f1SGleb Smirnoff tcp_lro_log(struct tcpcb *tp, const struct lro_ctrl *lc, 1564f9c93f1SGleb Smirnoff const struct lro_entry *le, const struct mbuf *m, 1574f9c93f1SGleb Smirnoff int frm, int32_t tcp_data_len, uint32_t th_seq, 1584f9c93f1SGleb Smirnoff uint32_t th_ack, uint16_t th_win) 1594f9c93f1SGleb Smirnoff { 1604f9c93f1SGleb Smirnoff if (tcp_bblogging_on(tp)) { 1614f9c93f1SGleb Smirnoff union tcp_log_stackspecific log; 1624f9c93f1SGleb Smirnoff struct timeval tv, btv; 1634f9c93f1SGleb Smirnoff uint32_t cts; 1644f9c93f1SGleb Smirnoff 1654f9c93f1SGleb Smirnoff cts = tcp_get_usecs(&tv); 1664f9c93f1SGleb Smirnoff memset(&log, 0, sizeof(union tcp_log_stackspecific)); 1674f9c93f1SGleb Smirnoff log.u_bbr.flex8 = frm; 1684f9c93f1SGleb Smirnoff log.u_bbr.flex1 = tcp_data_len; 1694f9c93f1SGleb Smirnoff if (m) 1704f9c93f1SGleb Smirnoff log.u_bbr.flex2 = m->m_pkthdr.len; 1714f9c93f1SGleb Smirnoff else 1724f9c93f1SGleb Smirnoff log.u_bbr.flex2 = 0; 1734f9c93f1SGleb Smirnoff if (le->m_head) { 1744f9c93f1SGleb Smirnoff log.u_bbr.flex3 = le->m_head->m_pkthdr.lro_nsegs; 1754f9c93f1SGleb Smirnoff log.u_bbr.flex4 = le->m_head->m_pkthdr.lro_tcp_d_len; 1764f9c93f1SGleb Smirnoff log.u_bbr.flex5 = le->m_head->m_pkthdr.len; 1774f9c93f1SGleb Smirnoff log.u_bbr.delRate = le->m_head->m_flags; 1784f9c93f1SGleb Smirnoff log.u_bbr.rttProp = le->m_head->m_pkthdr.rcv_tstmp; 1794f9c93f1SGleb Smirnoff } 1804f9c93f1SGleb Smirnoff log.u_bbr.inflight = th_seq; 1814f9c93f1SGleb Smirnoff log.u_bbr.delivered = th_ack; 1824f9c93f1SGleb Smirnoff log.u_bbr.timeStamp = cts; 1834f9c93f1SGleb Smirnoff log.u_bbr.epoch = le->next_seq; 1844f9c93f1SGleb Smirnoff log.u_bbr.lt_epoch = le->ack_seq; 1854f9c93f1SGleb Smirnoff log.u_bbr.pacing_gain = th_win; 1864f9c93f1SGleb Smirnoff log.u_bbr.cwnd_gain = le->window; 1874f9c93f1SGleb Smirnoff log.u_bbr.lost = curcpu; 1884f9c93f1SGleb Smirnoff log.u_bbr.cur_del_rate = (uintptr_t)m; 1894f9c93f1SGleb Smirnoff log.u_bbr.bw_inuse = (uintptr_t)le->m_head; 1904f9c93f1SGleb Smirnoff bintime2timeval(&lc->lro_last_queue_time, &btv); 1914f9c93f1SGleb Smirnoff log.u_bbr.flex6 = tcp_tv_to_usectick(&btv); 1924f9c93f1SGleb Smirnoff log.u_bbr.flex7 = le->compressed; 1934f9c93f1SGleb Smirnoff log.u_bbr.pacing_gain = le->uncompressed; 1944f9c93f1SGleb Smirnoff if (in_epoch(net_epoch_preempt)) 1954f9c93f1SGleb Smirnoff log.u_bbr.inhpts = 1; 1964f9c93f1SGleb Smirnoff else 1974f9c93f1SGleb Smirnoff log.u_bbr.inhpts = 0; 1984f9c93f1SGleb Smirnoff TCP_LOG_EVENTP(tp, NULL, &tptosocket(tp)->so_rcv, 1994f9c93f1SGleb Smirnoff &tptosocket(tp)->so_snd, 2004f9c93f1SGleb Smirnoff TCP_LOG_LRO, 0, 0, &log, false, &tv); 2014f9c93f1SGleb Smirnoff } 2024f9c93f1SGleb Smirnoff } 203*8caa2f53SMichael Tuexen #endif 2044f9c93f1SGleb Smirnoff 2054f9c93f1SGleb Smirnoff static struct mbuf * 2064f9c93f1SGleb Smirnoff tcp_lro_get_last_if_ackcmp(struct lro_ctrl *lc, struct lro_entry *le, 2074f9c93f1SGleb Smirnoff struct tcpcb *tp, int32_t *new_m, bool can_append_old_cmp) 2084f9c93f1SGleb Smirnoff { 2094f9c93f1SGleb Smirnoff struct mbuf *m; 2104f9c93f1SGleb Smirnoff 2114f9c93f1SGleb Smirnoff /* Look at the last mbuf if any in queue */ 2124f9c93f1SGleb Smirnoff if (can_append_old_cmp) { 2134f9c93f1SGleb Smirnoff m = STAILQ_LAST(&tp->t_inqueue, mbuf, m_stailqpkt); 2144f9c93f1SGleb Smirnoff if (m != NULL && (m->m_flags & M_ACKCMP) != 0) { 2154f9c93f1SGleb Smirnoff if (M_TRAILINGSPACE(m) >= sizeof(struct tcp_ackent)) { 216*8caa2f53SMichael Tuexen #ifdef TCP_BLACKBOX 2174f9c93f1SGleb Smirnoff tcp_lro_log(tp, lc, le, NULL, 23, 0, 0, 0, 0); 218*8caa2f53SMichael Tuexen #endif 2194f9c93f1SGleb Smirnoff *new_m = 0; 2204f9c93f1SGleb Smirnoff counter_u64_add(tcp_extra_mbuf, 1); 2214f9c93f1SGleb Smirnoff return (m); 2224f9c93f1SGleb Smirnoff } else { 2234f9c93f1SGleb Smirnoff /* Mark we ran out of space */ 2244f9c93f1SGleb Smirnoff tp->t_flags2 |= TF2_MBUF_L_ACKS; 2254f9c93f1SGleb Smirnoff } 2264f9c93f1SGleb Smirnoff } 2274f9c93f1SGleb Smirnoff } 2284f9c93f1SGleb Smirnoff /* Decide mbuf size. */ 229*8caa2f53SMichael Tuexen #ifdef TCP_BLACKBOX 2304f9c93f1SGleb Smirnoff tcp_lro_log(tp, lc, le, NULL, 21, 0, 0, 0, 0); 231*8caa2f53SMichael Tuexen #endif 2324f9c93f1SGleb Smirnoff if (tp->t_flags2 & TF2_MBUF_L_ACKS) 2334f9c93f1SGleb Smirnoff m = m_getcl(M_NOWAIT, MT_DATA, M_ACKCMP | M_PKTHDR); 2344f9c93f1SGleb Smirnoff else 2354f9c93f1SGleb Smirnoff m = m_gethdr(M_NOWAIT, MT_DATA); 2364f9c93f1SGleb Smirnoff 2374f9c93f1SGleb Smirnoff if (__predict_false(m == NULL)) { 2384f9c93f1SGleb Smirnoff counter_u64_add(tcp_would_have_but, 1); 2394f9c93f1SGleb Smirnoff return (NULL); 2404f9c93f1SGleb Smirnoff } 2414f9c93f1SGleb Smirnoff counter_u64_add(tcp_comp_total, 1); 2424f9c93f1SGleb Smirnoff m->m_pkthdr.rcvif = lc->ifp; 2434f9c93f1SGleb Smirnoff m->m_flags |= M_ACKCMP; 2444f9c93f1SGleb Smirnoff *new_m = 1; 2454f9c93f1SGleb Smirnoff return (m); 2464f9c93f1SGleb Smirnoff } 2474f9c93f1SGleb Smirnoff 2484f9c93f1SGleb Smirnoff /* 2494f9c93f1SGleb Smirnoff * Do BPF tap for either ACK_CMP packets or MBUF QUEUE type packets 2504f9c93f1SGleb Smirnoff * and strip all, but the IPv4/IPv6 header. 2514f9c93f1SGleb Smirnoff */ 2524f9c93f1SGleb Smirnoff static bool 2534f9c93f1SGleb Smirnoff do_bpf_strip_and_compress(struct tcpcb *tp, struct lro_ctrl *lc, 2544f9c93f1SGleb Smirnoff struct lro_entry *le, struct mbuf **pp, struct mbuf **cmp, 2554f9c93f1SGleb Smirnoff struct mbuf **mv_to, bool *should_wake, bool bpf_req, bool lagg_bpf_req, 2564f9c93f1SGleb Smirnoff struct ifnet *lagg_ifp, bool can_append_old_cmp) 2574f9c93f1SGleb Smirnoff { 2584f9c93f1SGleb Smirnoff union { 2594f9c93f1SGleb Smirnoff void *ptr; 2604f9c93f1SGleb Smirnoff struct ip *ip4; 2614f9c93f1SGleb Smirnoff struct ip6_hdr *ip6; 2624f9c93f1SGleb Smirnoff } l3; 2634f9c93f1SGleb Smirnoff struct mbuf *m; 2644f9c93f1SGleb Smirnoff struct mbuf *nm; 2654f9c93f1SGleb Smirnoff struct tcphdr *th; 2664f9c93f1SGleb Smirnoff struct tcp_ackent *ack_ent; 2674f9c93f1SGleb Smirnoff uint32_t *ts_ptr; 2684f9c93f1SGleb Smirnoff int32_t n_mbuf; 2694f9c93f1SGleb Smirnoff bool other_opts, can_compress; 2704f9c93f1SGleb Smirnoff uint8_t lro_type; 2714f9c93f1SGleb Smirnoff uint16_t iptos; 2724f9c93f1SGleb Smirnoff int tcp_hdr_offset; 2734f9c93f1SGleb Smirnoff int idx; 2744f9c93f1SGleb Smirnoff 2754f9c93f1SGleb Smirnoff /* Get current mbuf. */ 2764f9c93f1SGleb Smirnoff m = *pp; 2774f9c93f1SGleb Smirnoff 2784f9c93f1SGleb Smirnoff /* Let the BPF see the packet */ 2794f9c93f1SGleb Smirnoff if (__predict_false(bpf_req)) 2804f9c93f1SGleb Smirnoff ETHER_BPF_MTAP(lc->ifp, m); 2814f9c93f1SGleb Smirnoff 2824f9c93f1SGleb Smirnoff if (__predict_false(lagg_bpf_req)) 2834f9c93f1SGleb Smirnoff ETHER_BPF_MTAP(lagg_ifp, m); 2844f9c93f1SGleb Smirnoff 2854f9c93f1SGleb Smirnoff tcp_hdr_offset = m->m_pkthdr.lro_tcp_h_off; 2864f9c93f1SGleb Smirnoff lro_type = le->inner.data.lro_type; 2874f9c93f1SGleb Smirnoff switch (lro_type) { 2884f9c93f1SGleb Smirnoff case LRO_TYPE_NONE: 2894f9c93f1SGleb Smirnoff lro_type = le->outer.data.lro_type; 2904f9c93f1SGleb Smirnoff switch (lro_type) { 2914f9c93f1SGleb Smirnoff case LRO_TYPE_IPV4_TCP: 2924f9c93f1SGleb Smirnoff tcp_hdr_offset -= sizeof(*le->outer.ip4); 2934f9c93f1SGleb Smirnoff m->m_pkthdr.lro_etype = ETHERTYPE_IP; 29496ad6401SMichael Tuexen IP_PROBE(receive, NULL, NULL, le->outer.ip4, lc->ifp, 29596ad6401SMichael Tuexen le->outer.ip4, NULL); 2964f9c93f1SGleb Smirnoff break; 2974f9c93f1SGleb Smirnoff case LRO_TYPE_IPV6_TCP: 2984f9c93f1SGleb Smirnoff tcp_hdr_offset -= sizeof(*le->outer.ip6); 2994f9c93f1SGleb Smirnoff m->m_pkthdr.lro_etype = ETHERTYPE_IPV6; 30096ad6401SMichael Tuexen IP_PROBE(receive, NULL, NULL, le->outer.ip6, lc->ifp, 30196ad6401SMichael Tuexen NULL, le->outer.ip6); 3024f9c93f1SGleb Smirnoff break; 3034f9c93f1SGleb Smirnoff default: 3044f9c93f1SGleb Smirnoff goto compressed; 3054f9c93f1SGleb Smirnoff } 3064f9c93f1SGleb Smirnoff break; 3074f9c93f1SGleb Smirnoff case LRO_TYPE_IPV4_TCP: 30896ad6401SMichael Tuexen switch (le->outer.data.lro_type) { 30996ad6401SMichael Tuexen case LRO_TYPE_IPV4_UDP: 31096ad6401SMichael Tuexen IP_PROBE(receive, NULL, NULL, le->outer.ip4, lc->ifp, 31196ad6401SMichael Tuexen le->outer.ip4, NULL); 31296ad6401SMichael Tuexen UDP_PROBE(receive, NULL, NULL, le->outer.ip4, NULL, 31396ad6401SMichael Tuexen le->outer.udp); 31496ad6401SMichael Tuexen break; 31596ad6401SMichael Tuexen case LRO_TYPE_IPV6_UDP: 31696ad6401SMichael Tuexen IP_PROBE(receive, NULL, NULL, le->outer.ip6, lc->ifp, 31796ad6401SMichael Tuexen NULL, le->outer.ip6); 31896ad6401SMichael Tuexen UDP_PROBE(receive, NULL, NULL, le->outer.ip6, NULL, 31996ad6401SMichael Tuexen le->outer.udp); 32096ad6401SMichael Tuexen break; 32196ad6401SMichael Tuexen default: 32296ad6401SMichael Tuexen __assert_unreachable(); 32396ad6401SMichael Tuexen break; 32496ad6401SMichael Tuexen } 3254f9c93f1SGleb Smirnoff tcp_hdr_offset -= sizeof(*le->outer.ip4); 3264f9c93f1SGleb Smirnoff m->m_pkthdr.lro_etype = ETHERTYPE_IP; 32796ad6401SMichael Tuexen IP_PROBE(receive, NULL, NULL, le->inner.ip4, NULL, 32896ad6401SMichael Tuexen le->inner.ip4, NULL); 3294f9c93f1SGleb Smirnoff break; 3304f9c93f1SGleb Smirnoff case LRO_TYPE_IPV6_TCP: 33196ad6401SMichael Tuexen switch (le->outer.data.lro_type) { 33296ad6401SMichael Tuexen case LRO_TYPE_IPV4_UDP: 33396ad6401SMichael Tuexen IP_PROBE(receive, NULL, NULL, le->outer.ip4, lc->ifp, 33496ad6401SMichael Tuexen le->outer.ip4, NULL); 33596ad6401SMichael Tuexen UDP_PROBE(receive, NULL, NULL, le->outer.ip4, NULL, 33696ad6401SMichael Tuexen le->outer.udp); 33796ad6401SMichael Tuexen break; 33896ad6401SMichael Tuexen case LRO_TYPE_IPV6_UDP: 33996ad6401SMichael Tuexen IP_PROBE(receive, NULL, NULL, le->outer.ip6, lc->ifp, 34096ad6401SMichael Tuexen NULL, le->outer.ip6); 34196ad6401SMichael Tuexen UDP_PROBE(receive, NULL, NULL, le->outer.ip6, NULL, 34296ad6401SMichael Tuexen le->outer.udp); 34396ad6401SMichael Tuexen break; 34496ad6401SMichael Tuexen default: 34596ad6401SMichael Tuexen __assert_unreachable(); 34696ad6401SMichael Tuexen break; 34796ad6401SMichael Tuexen } 3484f9c93f1SGleb Smirnoff tcp_hdr_offset -= sizeof(*le->outer.ip6); 3494f9c93f1SGleb Smirnoff m->m_pkthdr.lro_etype = ETHERTYPE_IPV6; 35096ad6401SMichael Tuexen IP_PROBE(receive, NULL, NULL, le->inner.ip6, NULL, NULL, 35196ad6401SMichael Tuexen le->inner.ip6); 3524f9c93f1SGleb Smirnoff break; 3534f9c93f1SGleb Smirnoff default: 3544f9c93f1SGleb Smirnoff goto compressed; 3554f9c93f1SGleb Smirnoff } 3564f9c93f1SGleb Smirnoff 3574f9c93f1SGleb Smirnoff MPASS(tcp_hdr_offset >= 0); 3584f9c93f1SGleb Smirnoff 3594f9c93f1SGleb Smirnoff m_adj(m, tcp_hdr_offset); 3604f9c93f1SGleb Smirnoff m->m_flags |= M_LRO_EHDRSTRP; 3614f9c93f1SGleb Smirnoff m->m_flags &= ~M_ACKCMP; 3624f9c93f1SGleb Smirnoff m->m_pkthdr.lro_tcp_h_off -= tcp_hdr_offset; 3634f9c93f1SGleb Smirnoff 3644f9c93f1SGleb Smirnoff th = tcp_lro_get_th(m); 3654f9c93f1SGleb Smirnoff 3664f9c93f1SGleb Smirnoff th->th_sum = 0; /* TCP checksum is valid. */ 367f30c7d56SMichael Tuexen tcp_fields_to_host(th); 36896ad6401SMichael Tuexen TCP_PROBE5(receive, NULL, tp, m, tp, th); 3694f9c93f1SGleb Smirnoff 3704f9c93f1SGleb Smirnoff /* Check if ACK can be compressed */ 3714f9c93f1SGleb Smirnoff can_compress = tcp_lro_ack_valid(m, th, &ts_ptr, &other_opts); 3724f9c93f1SGleb Smirnoff 3734f9c93f1SGleb Smirnoff /* Now lets look at the should wake states */ 3744f9c93f1SGleb Smirnoff if ((other_opts == true) && 3754f9c93f1SGleb Smirnoff ((tp->t_flags2 & TF2_DONT_SACK_QUEUE) == 0)) { 3764f9c93f1SGleb Smirnoff /* 3774f9c93f1SGleb Smirnoff * If there are other options (SACK?) and the 3784f9c93f1SGleb Smirnoff * tcp endpoint has not expressly told us it does 3794f9c93f1SGleb Smirnoff * not care about SACKS, then we should wake up. 3804f9c93f1SGleb Smirnoff */ 3814f9c93f1SGleb Smirnoff *should_wake = true; 3824f9c93f1SGleb Smirnoff } else if (*should_wake == false) { 3834f9c93f1SGleb Smirnoff /* Wakeup override check if we are false here */ 3844f9c93f1SGleb Smirnoff *should_wake = tcp_lro_check_wake_status(tp); 3854f9c93f1SGleb Smirnoff } 3864f9c93f1SGleb Smirnoff /* Is the ack compressable? */ 3874f9c93f1SGleb Smirnoff if (can_compress == false) 3884f9c93f1SGleb Smirnoff goto done; 3894f9c93f1SGleb Smirnoff /* Does the TCP endpoint support ACK compression? */ 3904f9c93f1SGleb Smirnoff if ((tp->t_flags2 & TF2_MBUF_ACKCMP) == 0) 3914f9c93f1SGleb Smirnoff goto done; 3924f9c93f1SGleb Smirnoff 3934f9c93f1SGleb Smirnoff /* Lets get the TOS/traffic class field */ 3944f9c93f1SGleb Smirnoff l3.ptr = mtod(m, void *); 3954f9c93f1SGleb Smirnoff switch (lro_type) { 3964f9c93f1SGleb Smirnoff case LRO_TYPE_IPV4_TCP: 3974f9c93f1SGleb Smirnoff iptos = l3.ip4->ip_tos; 3984f9c93f1SGleb Smirnoff break; 3994f9c93f1SGleb Smirnoff case LRO_TYPE_IPV6_TCP: 4004f9c93f1SGleb Smirnoff iptos = IPV6_TRAFFIC_CLASS(l3.ip6); 4014f9c93f1SGleb Smirnoff break; 4024f9c93f1SGleb Smirnoff default: 4034f9c93f1SGleb Smirnoff iptos = 0; /* Keep compiler happy. */ 4044f9c93f1SGleb Smirnoff break; 4054f9c93f1SGleb Smirnoff } 4064f9c93f1SGleb Smirnoff /* Now lets get space if we don't have some already */ 4074f9c93f1SGleb Smirnoff if (*cmp == NULL) { 4084f9c93f1SGleb Smirnoff new_one: 4094f9c93f1SGleb Smirnoff nm = tcp_lro_get_last_if_ackcmp(lc, le, tp, &n_mbuf, 4104f9c93f1SGleb Smirnoff can_append_old_cmp); 4114f9c93f1SGleb Smirnoff if (__predict_false(nm == NULL)) 4124f9c93f1SGleb Smirnoff goto done; 4134f9c93f1SGleb Smirnoff *cmp = nm; 4144f9c93f1SGleb Smirnoff if (n_mbuf) { 4154f9c93f1SGleb Smirnoff /* 4164f9c93f1SGleb Smirnoff * Link in the new cmp ack to our in-order place, 4174f9c93f1SGleb Smirnoff * first set our cmp ack's next to where we are. 4184f9c93f1SGleb Smirnoff */ 4194f9c93f1SGleb Smirnoff nm->m_nextpkt = m; 4204f9c93f1SGleb Smirnoff (*pp) = nm; 4214f9c93f1SGleb Smirnoff /* 4224f9c93f1SGleb Smirnoff * Set it up so mv_to is advanced to our 4234f9c93f1SGleb Smirnoff * compressed ack. This way the caller can 4244f9c93f1SGleb Smirnoff * advance pp to the right place. 4254f9c93f1SGleb Smirnoff */ 4264f9c93f1SGleb Smirnoff *mv_to = nm; 4274f9c93f1SGleb Smirnoff /* 4284f9c93f1SGleb Smirnoff * Advance it here locally as well. 4294f9c93f1SGleb Smirnoff */ 4304f9c93f1SGleb Smirnoff pp = &nm->m_nextpkt; 4314f9c93f1SGleb Smirnoff } 4324f9c93f1SGleb Smirnoff } else { 4334f9c93f1SGleb Smirnoff /* We have one already we are working on */ 4344f9c93f1SGleb Smirnoff nm = *cmp; 4354f9c93f1SGleb Smirnoff if (M_TRAILINGSPACE(nm) < sizeof(struct tcp_ackent)) { 4364f9c93f1SGleb Smirnoff /* We ran out of space */ 4374f9c93f1SGleb Smirnoff tp->t_flags2 |= TF2_MBUF_L_ACKS; 4384f9c93f1SGleb Smirnoff goto new_one; 4394f9c93f1SGleb Smirnoff } 4404f9c93f1SGleb Smirnoff } 4414f9c93f1SGleb Smirnoff MPASS(M_TRAILINGSPACE(nm) >= sizeof(struct tcp_ackent)); 4424f9c93f1SGleb Smirnoff counter_u64_add(tcp_inp_lro_compressed, 1); 4434f9c93f1SGleb Smirnoff le->compressed++; 4444f9c93f1SGleb Smirnoff /* We can add in to the one on the tail */ 4454f9c93f1SGleb Smirnoff ack_ent = mtod(nm, struct tcp_ackent *); 4464f9c93f1SGleb Smirnoff idx = (nm->m_len / sizeof(struct tcp_ackent)); 4474f9c93f1SGleb Smirnoff build_ack_entry(&ack_ent[idx], th, m, ts_ptr, iptos); 4484f9c93f1SGleb Smirnoff 4494f9c93f1SGleb Smirnoff /* Bump the size of both pkt-hdr and len */ 4504f9c93f1SGleb Smirnoff nm->m_len += sizeof(struct tcp_ackent); 4514f9c93f1SGleb Smirnoff nm->m_pkthdr.len += sizeof(struct tcp_ackent); 4524f9c93f1SGleb Smirnoff compressed: 4534f9c93f1SGleb Smirnoff /* Advance to next mbuf before freeing. */ 4544f9c93f1SGleb Smirnoff *pp = m->m_nextpkt; 4554f9c93f1SGleb Smirnoff m->m_nextpkt = NULL; 4564f9c93f1SGleb Smirnoff m_freem(m); 4574f9c93f1SGleb Smirnoff return (true); 4584f9c93f1SGleb Smirnoff done: 4594f9c93f1SGleb Smirnoff counter_u64_add(tcp_uncomp_total, 1); 4604f9c93f1SGleb Smirnoff le->uncompressed++; 4614f9c93f1SGleb Smirnoff return (false); 4624f9c93f1SGleb Smirnoff } 4634f9c93f1SGleb Smirnoff 4644f9c93f1SGleb Smirnoff static void 4654f9c93f1SGleb Smirnoff tcp_queue_pkts(struct tcpcb *tp, struct lro_entry *le) 4664f9c93f1SGleb Smirnoff { 4674f9c93f1SGleb Smirnoff 4684f9c93f1SGleb Smirnoff INP_WLOCK_ASSERT(tptoinpcb(tp)); 4694f9c93f1SGleb Smirnoff 4704f9c93f1SGleb Smirnoff STAILQ_HEAD(, mbuf) q = { le->m_head, 4714f9c93f1SGleb Smirnoff &STAILQ_NEXT(le->m_last_mbuf, m_stailqpkt) }; 4724f9c93f1SGleb Smirnoff STAILQ_CONCAT(&tp->t_inqueue, &q); 4734f9c93f1SGleb Smirnoff le->m_head = NULL; 4744f9c93f1SGleb Smirnoff le->m_last_mbuf = NULL; 4754f9c93f1SGleb Smirnoff } 4764f9c93f1SGleb Smirnoff 4774f9c93f1SGleb Smirnoff static struct tcpcb * 4784f9c93f1SGleb Smirnoff tcp_lro_lookup(struct ifnet *ifp, struct lro_parser *pa) 4794f9c93f1SGleb Smirnoff { 4804f9c93f1SGleb Smirnoff struct inpcb *inp; 4814f9c93f1SGleb Smirnoff 482d1ce0121SMichael Tuexen CURVNET_ASSERT_SET(); 4834f9c93f1SGleb Smirnoff switch (pa->data.lro_type) { 4844f9c93f1SGleb Smirnoff #ifdef INET6 4854f9c93f1SGleb Smirnoff case LRO_TYPE_IPV6_TCP: 4864f9c93f1SGleb Smirnoff inp = in6_pcblookup(&V_tcbinfo, 4874f9c93f1SGleb Smirnoff &pa->data.s_addr.v6, 4884f9c93f1SGleb Smirnoff pa->data.s_port, 4894f9c93f1SGleb Smirnoff &pa->data.d_addr.v6, 4904f9c93f1SGleb Smirnoff pa->data.d_port, 4914f9c93f1SGleb Smirnoff INPLOOKUP_WLOCKPCB, 4924f9c93f1SGleb Smirnoff ifp); 4934f9c93f1SGleb Smirnoff break; 4944f9c93f1SGleb Smirnoff #endif 4954f9c93f1SGleb Smirnoff #ifdef INET 4964f9c93f1SGleb Smirnoff case LRO_TYPE_IPV4_TCP: 4974f9c93f1SGleb Smirnoff inp = in_pcblookup(&V_tcbinfo, 4984f9c93f1SGleb Smirnoff pa->data.s_addr.v4, 4994f9c93f1SGleb Smirnoff pa->data.s_port, 5004f9c93f1SGleb Smirnoff pa->data.d_addr.v4, 5014f9c93f1SGleb Smirnoff pa->data.d_port, 5024f9c93f1SGleb Smirnoff INPLOOKUP_WLOCKPCB, 5034f9c93f1SGleb Smirnoff ifp); 5044f9c93f1SGleb Smirnoff break; 5054f9c93f1SGleb Smirnoff #endif 5064f9c93f1SGleb Smirnoff default: 5074f9c93f1SGleb Smirnoff return (NULL); 5084f9c93f1SGleb Smirnoff } 5094f9c93f1SGleb Smirnoff 5104f9c93f1SGleb Smirnoff return (intotcpcb(inp)); 5114f9c93f1SGleb Smirnoff } 5124f9c93f1SGleb Smirnoff 5132c6fc36aSGleb Smirnoff static int 5142c6fc36aSGleb Smirnoff _tcp_lro_flush_tcphpts(struct lro_ctrl *lc, struct lro_entry *le) 5154f9c93f1SGleb Smirnoff { 5164f9c93f1SGleb Smirnoff struct tcpcb *tp; 5174f9c93f1SGleb Smirnoff struct mbuf **pp, *cmp, *mv_to; 5184f9c93f1SGleb Smirnoff struct ifnet *lagg_ifp; 5194f9c93f1SGleb Smirnoff bool bpf_req, lagg_bpf_req, should_wake, can_append_old_cmp; 5204f9c93f1SGleb Smirnoff 5214f9c93f1SGleb Smirnoff /* Check if packet doesn't belongs to our network interface. */ 5224f9c93f1SGleb Smirnoff if ((tcplro_stacks_wanting_mbufq == 0) || 5234f9c93f1SGleb Smirnoff (le->outer.data.vlan_id != 0) || 5244f9c93f1SGleb Smirnoff (le->inner.data.lro_type != LRO_TYPE_NONE)) 5254f9c93f1SGleb Smirnoff return (TCP_LRO_CANNOT); 5264f9c93f1SGleb Smirnoff 5274f9c93f1SGleb Smirnoff #ifdef INET6 5284f9c93f1SGleb Smirnoff /* 5294f9c93f1SGleb Smirnoff * Be proactive about unspecified IPv6 address in source. As 5304f9c93f1SGleb Smirnoff * we use all-zero to indicate unbounded/unconnected pcb, 5314f9c93f1SGleb Smirnoff * unspecified IPv6 address can be used to confuse us. 5324f9c93f1SGleb Smirnoff * 5334f9c93f1SGleb Smirnoff * Note that packets with unspecified IPv6 destination is 5344f9c93f1SGleb Smirnoff * already dropped in ip6_input. 5354f9c93f1SGleb Smirnoff */ 5364f9c93f1SGleb Smirnoff if (__predict_false(le->outer.data.lro_type == LRO_TYPE_IPV6_TCP && 5374f9c93f1SGleb Smirnoff IN6_IS_ADDR_UNSPECIFIED(&le->outer.data.s_addr.v6))) 5384f9c93f1SGleb Smirnoff return (TCP_LRO_CANNOT); 5394f9c93f1SGleb Smirnoff 5404f9c93f1SGleb Smirnoff if (__predict_false(le->inner.data.lro_type == LRO_TYPE_IPV6_TCP && 5414f9c93f1SGleb Smirnoff IN6_IS_ADDR_UNSPECIFIED(&le->inner.data.s_addr.v6))) 5424f9c93f1SGleb Smirnoff return (TCP_LRO_CANNOT); 5434f9c93f1SGleb Smirnoff #endif 544d1ce0121SMichael Tuexen 545d1ce0121SMichael Tuexen CURVNET_SET(lc->ifp->if_vnet); 546d1ce0121SMichael Tuexen /* 547d1ce0121SMichael Tuexen * Ensure that there are no packet filter hooks which would normally 548d1ce0121SMichael Tuexen * being triggered in ether_demux(), ip_input(), or ip6_input(). 549d1ce0121SMichael Tuexen */ 550d1ce0121SMichael Tuexen if ( 551d1ce0121SMichael Tuexen #ifdef INET 552d1ce0121SMichael Tuexen PFIL_HOOKED_IN(V_inet_pfil_head) || 553d1ce0121SMichael Tuexen #endif 554d1ce0121SMichael Tuexen #ifdef INET6 555d1ce0121SMichael Tuexen PFIL_HOOKED_IN(V_inet6_pfil_head) || 556d1ce0121SMichael Tuexen #endif 557d1ce0121SMichael Tuexen PFIL_HOOKED_IN(V_link_pfil_head)) { 558d1ce0121SMichael Tuexen CURVNET_RESTORE(); 559d1ce0121SMichael Tuexen return (TCP_LRO_CANNOT); 560d1ce0121SMichael Tuexen } 561d1ce0121SMichael Tuexen 5624f9c93f1SGleb Smirnoff /* Lookup inp, if any. Returns locked TCP inpcb. */ 5634f9c93f1SGleb Smirnoff tp = tcp_lro_lookup(lc->ifp, 5644f9c93f1SGleb Smirnoff (le->inner.data.lro_type == LRO_TYPE_NONE) ? &le->outer : &le->inner); 565d1ce0121SMichael Tuexen CURVNET_RESTORE(); 5664f9c93f1SGleb Smirnoff if (tp == NULL) 5674f9c93f1SGleb Smirnoff return (TCP_LRO_CANNOT); 5684f9c93f1SGleb Smirnoff 5694f9c93f1SGleb Smirnoff counter_u64_add(tcp_inp_lro_locks_taken, 1); 5704f9c93f1SGleb Smirnoff 5714f9c93f1SGleb Smirnoff /* Check if the inp is dead, Jim. */ 5724f9c93f1SGleb Smirnoff if (tp->t_state == TCPS_TIME_WAIT) { 5734f9c93f1SGleb Smirnoff INP_WUNLOCK(tptoinpcb(tp)); 5744f9c93f1SGleb Smirnoff return (TCP_LRO_CANNOT); 5754f9c93f1SGleb Smirnoff } 5764f9c93f1SGleb Smirnoff if (tp->t_lro_cpu == HPTS_CPU_NONE && lc->lro_cpu_is_set == 1) 5774f9c93f1SGleb Smirnoff tp->t_lro_cpu = lc->lro_last_cpu; 5784f9c93f1SGleb Smirnoff /* Check if the transport doesn't support the needed optimizations. */ 5794f9c93f1SGleb Smirnoff if ((tp->t_flags2 & (TF2_SUPPORTS_MBUFQ | TF2_MBUF_ACKCMP)) == 0) { 5804f9c93f1SGleb Smirnoff INP_WUNLOCK(tptoinpcb(tp)); 5814f9c93f1SGleb Smirnoff return (TCP_LRO_CANNOT); 5824f9c93f1SGleb Smirnoff } 5834f9c93f1SGleb Smirnoff 5844f9c93f1SGleb Smirnoff if (tp->t_flags2 & TF2_MBUF_QUEUE_READY) 5854f9c93f1SGleb Smirnoff should_wake = false; 5864f9c93f1SGleb Smirnoff else 5874f9c93f1SGleb Smirnoff should_wake = true; 5884f9c93f1SGleb Smirnoff /* Check if packets should be tapped to BPF. */ 5894f9c93f1SGleb Smirnoff bpf_req = bpf_peers_present(lc->ifp->if_bpf); 5904f9c93f1SGleb Smirnoff lagg_bpf_req = false; 5914f9c93f1SGleb Smirnoff lagg_ifp = NULL; 5924f9c93f1SGleb Smirnoff if (lc->ifp->if_type == IFT_IEEE8023ADLAG || 5934f9c93f1SGleb Smirnoff lc->ifp->if_type == IFT_INFINIBANDLAG) { 5944f9c93f1SGleb Smirnoff struct lagg_port *lp = lc->ifp->if_lagg; 5954f9c93f1SGleb Smirnoff struct lagg_softc *sc = lp->lp_softc; 5964f9c93f1SGleb Smirnoff 5974f9c93f1SGleb Smirnoff lagg_ifp = sc->sc_ifp; 5984f9c93f1SGleb Smirnoff if (lagg_ifp != NULL) 5994f9c93f1SGleb Smirnoff lagg_bpf_req = bpf_peers_present(lagg_ifp->if_bpf); 6004f9c93f1SGleb Smirnoff } 6014f9c93f1SGleb Smirnoff 6024f9c93f1SGleb Smirnoff /* Strip and compress all the incoming packets. */ 6034f9c93f1SGleb Smirnoff can_append_old_cmp = true; 6044f9c93f1SGleb Smirnoff cmp = NULL; 6054f9c93f1SGleb Smirnoff for (pp = &le->m_head; *pp != NULL; ) { 6064f9c93f1SGleb Smirnoff mv_to = NULL; 6074f9c93f1SGleb Smirnoff if (do_bpf_strip_and_compress(tp, lc, le, pp, &cmp, &mv_to, 6084f9c93f1SGleb Smirnoff &should_wake, bpf_req, lagg_bpf_req, lagg_ifp, 6094f9c93f1SGleb Smirnoff can_append_old_cmp) == false) { 6104f9c93f1SGleb Smirnoff /* Advance to next mbuf. */ 6114f9c93f1SGleb Smirnoff pp = &(*pp)->m_nextpkt; 6124f9c93f1SGleb Smirnoff /* 6134f9c93f1SGleb Smirnoff * Once we have appended we can't look in the pending 6144f9c93f1SGleb Smirnoff * inbound packets for a compressed ack to append to. 6154f9c93f1SGleb Smirnoff */ 6164f9c93f1SGleb Smirnoff can_append_old_cmp = false; 6174f9c93f1SGleb Smirnoff /* 6184f9c93f1SGleb Smirnoff * Once we append we also need to stop adding to any 6194f9c93f1SGleb Smirnoff * compressed ack we were remembering. A new cmp 6204f9c93f1SGleb Smirnoff * ack will be required. 6214f9c93f1SGleb Smirnoff */ 6224f9c93f1SGleb Smirnoff cmp = NULL; 623*8caa2f53SMichael Tuexen #ifdef TCP_BLACKBOX 6244f9c93f1SGleb Smirnoff tcp_lro_log(tp, lc, le, NULL, 25, 0, 0, 0, 0); 625*8caa2f53SMichael Tuexen #endif 6264f9c93f1SGleb Smirnoff } else if (mv_to != NULL) { 6274f9c93f1SGleb Smirnoff /* We are asked to move pp up */ 6284f9c93f1SGleb Smirnoff pp = &mv_to->m_nextpkt; 629*8caa2f53SMichael Tuexen #ifdef TCP_BLACKBOX 6304f9c93f1SGleb Smirnoff tcp_lro_log(tp, lc, le, NULL, 24, 0, 0, 0, 0); 6314f9c93f1SGleb Smirnoff } else 6324f9c93f1SGleb Smirnoff tcp_lro_log(tp, lc, le, NULL, 26, 0, 0, 0, 0); 633*8caa2f53SMichael Tuexen #else 634*8caa2f53SMichael Tuexen } 635*8caa2f53SMichael Tuexen #endif 6364f9c93f1SGleb Smirnoff } 6374f9c93f1SGleb Smirnoff /* Update "m_last_mbuf", if any. */ 6384f9c93f1SGleb Smirnoff if (pp == &le->m_head) 6394f9c93f1SGleb Smirnoff le->m_last_mbuf = *pp; 6404f9c93f1SGleb Smirnoff else 6414f9c93f1SGleb Smirnoff le->m_last_mbuf = __containerof(pp, struct mbuf, m_nextpkt); 6424f9c93f1SGleb Smirnoff 6434f9c93f1SGleb Smirnoff /* Check if any data mbufs left. */ 6444f9c93f1SGleb Smirnoff if (le->m_head != NULL) { 6454f9c93f1SGleb Smirnoff counter_u64_add(tcp_inp_lro_direct_queue, 1); 646*8caa2f53SMichael Tuexen #ifdef TCP_BLACKBOX 6474f9c93f1SGleb Smirnoff tcp_lro_log(tp, lc, le, NULL, 22, 1, tp->t_flags2, 0, 1); 648*8caa2f53SMichael Tuexen #endif 6494f9c93f1SGleb Smirnoff tcp_queue_pkts(tp, le); 6504f9c93f1SGleb Smirnoff } 6514f9c93f1SGleb Smirnoff if (should_wake) { 6524f9c93f1SGleb Smirnoff /* Wakeup */ 6534f9c93f1SGleb Smirnoff counter_u64_add(tcp_inp_lro_wokeup_queue, 1); 6544f9c93f1SGleb Smirnoff if ((*tp->t_fb->tfb_do_queued_segments)(tp, 0)) 6554f9c93f1SGleb Smirnoff /* TCP cb gone and unlocked. */ 6564f9c93f1SGleb Smirnoff return (0); 6574f9c93f1SGleb Smirnoff } 6584f9c93f1SGleb Smirnoff INP_WUNLOCK(tptoinpcb(tp)); 6594f9c93f1SGleb Smirnoff 6604f9c93f1SGleb Smirnoff return (0); /* Success. */ 6614f9c93f1SGleb Smirnoff } 6622c6fc36aSGleb Smirnoff 6632c6fc36aSGleb Smirnoff void 6642c6fc36aSGleb Smirnoff tcp_lro_hpts_init(void) 6652c6fc36aSGleb Smirnoff { 6662c6fc36aSGleb Smirnoff tcp_lro_flush_tcphpts = _tcp_lro_flush_tcphpts; 6672c6fc36aSGleb Smirnoff } 66848b55a7cSGleb Smirnoff 66948b55a7cSGleb Smirnoff void 67048b55a7cSGleb Smirnoff tcp_lro_hpts_uninit(void) 67148b55a7cSGleb Smirnoff { 67248b55a7cSGleb Smirnoff atomic_store_ptr(&tcp_lro_flush_tcphpts, NULL); 67348b55a7cSGleb Smirnoff } 674