Home | History | Annotate | Line # | Download | only in cxgb
      1  1.1   jklos /**************************************************************************
      2  1.1   jklos 
      3  1.1   jklos Copyright (c) 2007, Chelsio Inc.
      4  1.1   jklos All rights reserved.
      5  1.1   jklos 
      6  1.1   jklos Redistribution and use in source and binary forms, with or without
      7  1.1   jklos modification, are permitted provided that the following conditions are met:
      8  1.1   jklos 
      9  1.1   jklos  1. Redistributions of source code must retain the above copyright notice,
     10  1.1   jklos     this list of conditions and the following disclaimer.
     11  1.1   jklos 
     12  1.1   jklos 2. Neither the name of the Chelsio Corporation nor the names of its
     13  1.1   jklos     contributors may be used to endorse or promote products derived from
     14  1.1   jklos     this software without specific prior written permission.
     15  1.1   jklos 
     16  1.1   jklos THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     17  1.1   jklos AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  1.1   jklos IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  1.1   jklos ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
     20  1.1   jklos LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  1.1   jklos CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  1.1   jklos SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  1.1   jklos INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  1.1   jklos CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  1.1   jklos ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  1.1   jklos POSSIBILITY OF SUCH DAMAGE.
     27  1.1   jklos 
     28  1.1   jklos ***************************************************************************/
     29  1.1   jklos 
     30  1.1   jklos #include <sys/cdefs.h>
     31  1.2  dyoung __KERNEL_RCSID(0, "$NetBSD: cxgb_lro.c,v 1.2 2011/05/18 01:01:59 dyoung Exp $");
     32  1.1   jklos 
     33  1.1   jklos 
     34  1.1   jklos #include <sys/param.h>
     35  1.1   jklos #include <sys/systm.h>
     36  1.1   jklos #include <sys/kernel.h>
     37  1.1   jklos #include <sys/conf.h>
     38  1.2  dyoung #include <sys/bus.h>
     39  1.1   jklos #include <sys/queue.h>
     40  1.1   jklos 
     41  1.1   jklos #include <netinet/in_systm.h>
     42  1.1   jklos #include <netinet/in.h>
     43  1.1   jklos #include <netinet/ip.h>
     44  1.1   jklos #include <netinet/tcp.h>
     45  1.1   jklos 
     46  1.1   jklos 
     47  1.1   jklos #ifdef CONFIG_DEFINED
     48  1.1   jklos #include <dev/pci/cxgb/cxgb_include.h>
     49  1.1   jklos 
     50  1.1   jklos #include <machine/in_cksum.h>
     51  1.1   jklos #endif
     52  1.1   jklos 
     53  1.1   jklos #include "cxgb_include.h"
     54  1.1   jklos 
     55  1.1   jklos #ifndef M_LRO
     56  1.1   jklos #define M_LRO    0x0200
     57  1.1   jklos #endif
     58  1.1   jklos 
     59  1.1   jklos #ifdef DEBUG
     60  1.1   jklos #define MBUF_HEADER_CHECK(m) do { \
     61  1.1   jklos     if ((m->m_len == 0) || (m->m_pkthdr.len == 0)   \
     62  1.1   jklos         || ((m->m_flags & M_PKTHDR) == 0))              \
     63  1.1   jklos         panic("lro_flush_session - mbuf len=%d pktlen=%d flags=0x%x\n", \
     64  1.1   jklos             m->m_len, m->m_pkthdr.len, m->m_flags); \
     65  1.1   jklos     if ((m->m_flags & M_PKTHDR) == 0)               \
     66  1.1   jklos         panic("first mbuf is not packet header - flags=0x%x\n", \
     67  1.1   jklos             m->m_flags);  \
     68  1.1   jklos     if ((m->m_len < ETHER_HDR_LEN) || (m->m_pkthdr.len < ETHER_HDR_LEN)) \
     69  1.1   jklos         panic("packet too small len=%d pktlen=%d\n", \
     70  1.1   jklos             m->m_len, m->m_pkthdr.len);\
     71  1.1   jklos } while (0)
     72  1.1   jklos #else
     73  1.1   jklos #define MBUF_HEADER_CHECK(m)
     74  1.1   jklos #endif
     75  1.1   jklos 
     76  1.1   jklos #define IPH_OFFSET (2 + sizeof (struct cpl_rx_pkt) + ETHER_HDR_LEN)
     77  1.1   jklos #define LRO_SESSION_IDX_HINT_HASH(hash) (hash & (MAX_LRO_SES - 1))
     78  1.1   jklos #define LRO_IDX_INC(idx) idx = (idx + 1) & (MAX_LRO_SES - 1)
     79  1.1   jklos 
     80  1.1   jklos static __inline int
     81  1.1   jklos lro_match(struct mbuf *m, struct ip *ih, struct tcphdr *th)
     82  1.1   jklos {
     83  1.1   jklos     struct ip *sih = (struct ip *)(mtod(m, uint8_t *) + IPH_OFFSET);
     84  1.1   jklos     struct tcphdr *sth = (struct tcphdr *) (sih + 1);
     85  1.1   jklos 
     86  1.1   jklos     return (th->th_sport == sth->th_sport &&
     87  1.1   jklos         th->th_dport == sth->th_dport &&
     88  1.1   jklos         ih->ip_src.s_addr == sih->ip_src.s_addr &&
     89  1.1   jklos         ih->ip_dst.s_addr == sih->ip_dst.s_addr);
     90  1.1   jklos }
     91  1.1   jklos 
     92  1.1   jklos static __inline struct t3_lro_session *
     93  1.1   jklos lro_lookup(struct lro_state *l, int idx, struct ip *ih, struct tcphdr *th)
     94  1.1   jklos {
     95  1.1   jklos     struct t3_lro_session *s = NULL;
     96  1.1   jklos     int active = l->nactive;
     97  1.1   jklos 
     98  1.1   jklos     while (active) {
     99  1.1   jklos         s = &l->sess[idx];
    100  1.1   jklos         if (s->head) {
    101  1.1   jklos             if (lro_match(s->head, ih, th))
    102  1.1   jklos                 break;
    103  1.1   jklos             active--;
    104  1.1   jklos         }
    105  1.1   jklos         LRO_IDX_INC(idx);
    106  1.1   jklos     }
    107  1.1   jklos 
    108  1.1   jklos     return (s);
    109  1.1   jklos }
    110  1.1   jklos 
    111  1.1   jklos static __inline int
    112  1.1   jklos can_lro_packet(struct cpl_rx_pkt *cpl, unsigned int rss_hi)
    113  1.1   jklos {
    114  1.1   jklos     struct ether_header *eh = (struct ether_header *)(cpl + 1);
    115  1.1   jklos     struct ip *ih = (struct ip *)(eh + 1);
    116  1.1   jklos 
    117  1.1   jklos     /*
    118  1.1   jklos      * XXX VLAN support?
    119  1.1   jklos      */
    120  1.1   jklos     if (__predict_false(G_HASHTYPE(ntohl(rss_hi)) != RSS_HASH_4_TUPLE ||
    121  1.1   jklos              (*((uint8_t *)cpl + 1) & 0x90) != 0x10 ||
    122  1.1   jklos              cpl->csum != 0xffff || eh->ether_type != ntohs(ETHERTYPE_IP) ||
    123  1.1   jklos              ih->ip_hl != (sizeof (*ih) >> 2))) {
    124  1.1   jklos         return 0;
    125  1.1   jklos     }
    126  1.1   jklos 
    127  1.1   jklos     return 1;
    128  1.1   jklos }
    129  1.1   jklos 
    130  1.1   jklos static int
    131  1.1   jklos can_lro_tcpsegment(struct tcphdr *th)
    132  1.1   jklos {
    133  1.1   jklos     int olen = (th->th_off << 2) - sizeof (*th);
    134  1.1   jklos     u8 control_bits = *((u8 *)th + 13);
    135  1.1   jklos 
    136  1.1   jklos     if (__predict_false((control_bits & 0xB7) != 0x10))
    137  1.1   jklos         goto no_lro;
    138  1.1   jklos 
    139  1.1   jklos     if (olen) {
    140  1.1   jklos         uint32_t *ptr = (u32 *)(th + 1);
    141  1.1   jklos         if (__predict_false(olen != TCPOLEN_TSTAMP_APPA ||
    142  1.1   jklos                  *ptr != ntohl((TCPOPT_NOP << 24) |
    143  1.1   jklos                        (TCPOPT_NOP << 16) |
    144  1.1   jklos                        (TCPOPT_TIMESTAMP << 8) |
    145  1.1   jklos                         TCPOLEN_TIMESTAMP)))
    146  1.1   jklos             goto no_lro;
    147  1.1   jklos     }
    148  1.1   jklos 
    149  1.1   jklos     return 1;
    150  1.1   jklos 
    151  1.1   jklos  no_lro:
    152  1.1   jklos     return 0;
    153  1.1   jklos }
    154  1.1   jklos 
    155  1.1   jklos static __inline void
    156  1.1   jklos lro_new_session_init(struct t3_lro_session *s, struct mbuf *m)
    157  1.1   jklos {
    158  1.1   jklos     struct ip *ih = (struct ip *)(mtod(m, uint8_t *) + IPH_OFFSET);
    159  1.1   jklos     struct tcphdr *th = (struct tcphdr *) (ih + 1);
    160  1.1   jklos     int ip_len = ntohs(ih->ip_len);
    161  1.1   jklos 
    162  1.1   jklos     DPRINTF("%s(s=%p, m=%p)\n", __func__, s, m);
    163  1.1   jklos 
    164  1.1   jklos     s->head = m;
    165  1.1   jklos 
    166  1.1   jklos     MBUF_HEADER_CHECK(m);
    167  1.1   jklos     s->ip_len = ip_len;
    168  1.1   jklos     s->seq = ntohl(th->th_seq) + ip_len - sizeof(*ih) - (th->th_off << 2);
    169  1.1   jklos 
    170  1.1   jklos }
    171  1.1   jklos 
    172  1.1   jklos static void
    173  1.1   jklos lro_flush_session(struct sge_qset *qs, struct t3_lro_session *s, struct mbuf *m)
    174  1.1   jklos {
    175  1.1   jklos     struct lro_state *l = &qs->lro;
    176  1.1   jklos     struct mbuf *sm = s->head;
    177  1.1   jklos     struct ip *ih = (struct ip *)(mtod(sm, uint8_t *) + IPH_OFFSET);
    178  1.1   jklos 
    179  1.1   jklos 
    180  1.1   jklos     DPRINTF("%s(qs=%p, s=%p, ", __func__,
    181  1.1   jklos         qs, s);
    182  1.1   jklos 
    183  1.1   jklos     if (m)
    184  1.1   jklos         DPRINTF("m=%p)\n", m);
    185  1.1   jklos     else
    186  1.1   jklos         DPRINTF("m=NULL)\n");
    187  1.1   jklos 
    188  1.1   jklos     ih->ip_len = htons(s->ip_len);
    189  1.1   jklos     ih->ip_sum = 0;
    190  1.1   jklos     ih->ip_sum = in_cksum_hdr(ih);
    191  1.1   jklos 
    192  1.1   jklos     MBUF_HEADER_CHECK(sm);
    193  1.1   jklos 
    194  1.1   jklos     sm->m_flags |= M_LRO;
    195  1.1   jklos     t3_rx_eth(qs->port->adapter, &qs->rspq, sm, 2);
    196  1.1   jklos 
    197  1.1   jklos     if (m) {
    198  1.1   jklos         s->head = m;
    199  1.1   jklos         lro_new_session_init(s, m);
    200  1.1   jklos     } else {
    201  1.1   jklos         s->head = NULL;
    202  1.1   jklos         l->nactive--;
    203  1.1   jklos     }
    204  1.1   jklos 
    205  1.1   jklos     qs->port_stats[SGE_PSTATS_LRO_FLUSHED]++;
    206  1.1   jklos }
    207  1.1   jklos 
    208  1.1   jklos static __inline struct t3_lro_session *
    209  1.1   jklos lro_new_session(struct sge_qset *qs, struct mbuf *m, uint32_t rss_hash)
    210  1.1   jklos {
    211  1.1   jklos     struct lro_state *l = &qs->lro;
    212  1.1   jklos     int idx = LRO_SESSION_IDX_HINT_HASH(rss_hash);
    213  1.1   jklos     struct t3_lro_session *s = &l->sess[idx];
    214  1.1   jklos 
    215  1.1   jklos     DPRINTF("%s(qs=%p,  m=%p, rss_hash=0x%x)\n", __func__,
    216  1.1   jklos         qs, m, rss_hash);
    217  1.1   jklos 
    218  1.1   jklos     if (__predict_true(!s->head))
    219  1.1   jklos         goto done;
    220  1.1   jklos 
    221  1.1   jklos     if (l->nactive > MAX_LRO_SES)
    222  1.1   jklos         panic("MAX_LRO_PER_QSET exceeded");
    223  1.1   jklos 
    224  1.1   jklos     if (l->nactive == MAX_LRO_SES) {
    225  1.1   jklos         lro_flush_session(qs, s, m);
    226  1.1   jklos         qs->port_stats[SGE_PSTATS_LRO_X_STREAMS]++;
    227  1.1   jklos         return s;
    228  1.1   jklos     }
    229  1.1   jklos 
    230  1.1   jklos     while (1) {
    231  1.1   jklos         LRO_IDX_INC(idx);
    232  1.1   jklos         s = &l->sess[idx];
    233  1.1   jklos         if (!s->head)
    234  1.1   jklos             break;
    235  1.1   jklos     }
    236  1.1   jklos done:
    237  1.1   jklos     lro_new_session_init(s, m);
    238  1.1   jklos     l->nactive++;
    239  1.1   jklos 
    240  1.1   jklos     return s;
    241  1.1   jklos }
    242  1.1   jklos 
    243  1.1   jklos static __inline int
    244  1.1   jklos lro_update_session(struct t3_lro_session *s, struct mbuf *m)
    245  1.1   jklos {
    246  1.1   jklos     struct mbuf *sm = s->head;
    247  1.1   jklos     struct cpl_rx_pkt *cpl = (struct cpl_rx_pkt *)(mtod(sm, uint8_t *) + 2);
    248  1.1   jklos     struct cpl_rx_pkt *ncpl = (struct cpl_rx_pkt *)(mtod(m, uint8_t *) + 2);
    249  1.1   jklos     struct ip *nih = (struct ip *)(mtod(m, uint8_t *) + IPH_OFFSET);
    250  1.1   jklos     struct tcphdr *th, *nth = (struct tcphdr *)(nih + 1);
    251  1.1   jklos     uint32_t seq = ntohl(nth->th_seq);
    252  1.1   jklos     int plen, tcpiphlen, olen = (nth->th_off << 2) - sizeof (*nth);
    253  1.1   jklos 
    254  1.1   jklos 
    255  1.1   jklos     DPRINTF("%s(s=%p,  m=%p)\n", __func__, s, m);
    256  1.1   jklos     if (cpl->vlan_valid && cpl->vlan != ncpl->vlan) {
    257  1.1   jklos         return -1;
    258  1.1   jklos     }
    259  1.1   jklos     if (__predict_false(seq != s->seq)) {
    260  1.1   jklos         DPRINTF("sequence mismatch\n");
    261  1.1   jklos         return -1;
    262  1.1   jklos     }
    263  1.1   jklos 
    264  1.1   jklos     MBUF_HEADER_CHECK(sm);
    265  1.1   jklos     th = (struct tcphdr *)(mtod(sm, uint8_t *) + IPH_OFFSET + sizeof (struct ip));
    266  1.1   jklos 
    267  1.1   jklos     if (olen) {
    268  1.1   jklos         uint32_t *ptr = (uint32_t *)(th + 1);
    269  1.1   jklos         uint32_t *nptr = (uint32_t *)(nth + 1);
    270  1.1   jklos 
    271  1.1   jklos         if (__predict_false(ntohl(*(ptr + 1)) > ntohl(*(nptr + 1)) ||
    272  1.1   jklos                  !*(nptr + 2))) {
    273  1.1   jklos             return -1;
    274  1.1   jklos         }
    275  1.1   jklos         *(ptr + 1) = *(nptr + 1);
    276  1.1   jklos         *(ptr + 2) = *(nptr + 2);
    277  1.1   jklos     }
    278  1.1   jklos     th->th_ack = nth->th_ack;
    279  1.1   jklos     th->th_win = nth->th_win;
    280  1.1   jklos 
    281  1.1   jklos     tcpiphlen = (nth->th_off << 2) + sizeof (*nih);
    282  1.1   jklos     plen = ntohs(nih->ip_len) - tcpiphlen;
    283  1.1   jklos     s->seq += plen;
    284  1.1   jklos     s->ip_len += plen;
    285  1.1   jklos     sm->m_pkthdr.len += plen;
    286  1.1   jklos 
    287  1.1   jklos     /*
    288  1.1   jklos      * XXX FIX ME
    289  1.1   jklos      *
    290  1.1   jklos      *
    291  1.1   jklos      */
    292  1.1   jklos 
    293  1.1   jklos #if 0
    294  1.1   jklos     /* XXX this I *do not* understand */
    295  1.1   jklos     if (plen > skb_shinfo(s->skb)->gso_size)
    296  1.1   jklos         skb_shinfo(s->skb)->gso_size = plen;
    297  1.1   jklos #endif
    298  1.1   jklos     DPRINTF("m_adj(%d)\n", (int)(IPH_OFFSET + tcpiphlen));
    299  1.1   jklos     m_adj(m, IPH_OFFSET + tcpiphlen);
    300  1.1   jklos #if 0
    301  1.1   jklos     if (__predict_false(!skb_shinfo(s->skb)->frag_list))
    302  1.1   jklos         skb_shinfo(s->skb)->frag_list = skb;
    303  1.1   jklos 
    304  1.1   jklos #endif
    305  1.1   jklos 
    306  1.1   jklos #if 0
    307  1.1   jklos 
    308  1.1   jklos     /*
    309  1.1   jklos      * XXX we really need to be able to
    310  1.1   jklos      * support vectors of buffers in FreeBSD
    311  1.1   jklos      */
    312  1.1   jklos     int nr = skb_shinfo(s->skb)->nr_frags;
    313  1.1   jklos     skb_shinfo(s->skb)->frags[nr].page = frag->page;
    314  1.1   jklos     skb_shinfo(s->skb)->frags[nr].page_offset =
    315  1.1   jklos         frag->page_offset + IPH_OFFSET + tcpiphlen;
    316  1.1   jklos     skb_shinfo(s->skb)->frags[nr].size = plen;
    317  1.1   jklos     skb_shinfo(s->skb)->nr_frags = ++nr;
    318  1.1   jklos 
    319  1.1   jklos #endif
    320  1.1   jklos     return (0);
    321  1.1   jklos }
    322  1.1   jklos 
    323  1.1   jklos void
    324  1.1   jklos t3_rx_eth_lro(adapter_t *adap, struct sge_rspq *rq, struct mbuf *m,
    325  1.1   jklos     int ethpad, uint32_t rss_hash, uint32_t rss_csum, int lro)
    326  1.1   jklos {
    327  1.1   jklos     struct sge_qset *qs = rspq_to_qset(rq);
    328  1.1   jklos     struct cpl_rx_pkt *cpl = (struct cpl_rx_pkt *)(mtod(m, uint8_t *) + ethpad);
    329  1.1   jklos     struct ether_header *eh = (struct ether_header *)(cpl + 1);
    330  1.1   jklos     struct ip *ih;
    331  1.1   jklos     struct tcphdr *th;
    332  1.1   jklos     struct t3_lro_session *s = NULL;
    333  1.1   jklos 
    334  1.1   jklos     if (lro == 0)
    335  1.1   jklos         goto no_lro;
    336  1.1   jklos 
    337  1.1   jklos     if (!can_lro_packet(cpl, rss_csum))
    338  1.1   jklos         goto no_lro;
    339  1.1   jklos 
    340  1.1   jklos     ih = (struct ip *)(eh + 1);
    341  1.1   jklos     th = (struct tcphdr *)(ih + 1);
    342  1.1   jklos 
    343  1.1   jklos     s = lro_lookup(&qs->lro,
    344  1.1   jklos         LRO_SESSION_IDX_HINT_HASH(rss_hash), ih, th);
    345  1.1   jklos 
    346  1.1   jklos     if (__predict_false(!can_lro_tcpsegment(th))) {
    347  1.1   jklos         goto no_lro;
    348  1.1   jklos     } else if (__predict_false(!s)) {
    349  1.1   jklos         s = lro_new_session(qs, m, rss_hash);
    350  1.1   jklos     } else {
    351  1.1   jklos         if (lro_update_session(s, m)) {
    352  1.1   jklos             lro_flush_session(qs, s, m);
    353  1.1   jklos         }
    354  1.1   jklos #ifdef notyet
    355  1.1   jklos         if (__predict_false(s->head->m_pkthdr.len + pi->ifp->if_mtu > 65535)) {
    356  1.1   jklos             lro_flush_session(qs, s, NULL);
    357  1.1   jklos         }
    358  1.1   jklos #endif
    359  1.1   jklos     }
    360  1.1   jklos 
    361  1.1   jklos     qs->port_stats[SGE_PSTATS_LRO_QUEUED]++;
    362  1.1   jklos     return;
    363  1.1   jklos no_lro:
    364  1.1   jklos     if (s)
    365  1.1   jklos         lro_flush_session(qs, s, NULL);
    366  1.1   jklos 
    367  1.1   jklos     if (m->m_len == 0 || m->m_pkthdr.len == 0 || (m->m_flags & M_PKTHDR) == 0)
    368  1.1   jklos         DPRINTF("rx_eth_lro mbuf len=%d pktlen=%d flags=0x%x\n",
    369  1.1   jklos             m->m_len, m->m_pkthdr.len, m->m_flags);
    370  1.1   jklos 
    371  1.1   jklos     t3_rx_eth(adap, rq, m, ethpad);
    372  1.1   jklos }
    373  1.1   jklos 
    374  1.1   jklos void
    375  1.1   jklos t3_lro_flush(adapter_t *adap, struct sge_qset *qs, struct lro_state *state)
    376  1.1   jklos {
    377  1.1   jklos     unsigned int idx = state->active_idx;
    378  1.1   jklos 
    379  1.1   jklos     while (state->nactive) {
    380  1.1   jklos         struct t3_lro_session *s = &state->sess[idx];
    381  1.1   jklos 
    382  1.1   jklos         if (s->head)
    383  1.1   jklos             lro_flush_session(qs, s, NULL);
    384  1.1   jklos         LRO_IDX_INC(idx);
    385  1.1   jklos     }
    386  1.1   jklos }
    387