cxgb_lro.c revision 1.2 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