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