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