Blame view

net/ipv4/inet_lro.c 13.6 KB
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  /*
   *  linux/net/ipv4/inet_lro.c
   *
   *  Large Receive Offload (ipv4 / tcp)
   *
   *  (C) Copyright IBM Corp. 2007
   *
   *  Authors:
   *       Jan-Bernd Themann <themann@de.ibm.com>
   *       Christoph Raisch <raisch@de.ibm.com>
   *
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2, or (at your option)
   * any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */
  
  
  #include <linux/module.h>
  #include <linux/if_vlan.h>
  #include <linux/inet_lro.h>
  
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Jan-Bernd Themann <themann@de.ibm.com>");
  MODULE_DESCRIPTION("Large Receive Offload (ipv4 / tcp)");
  
  #define TCP_HDR_LEN(tcph) (tcph->doff << 2)
  #define IP_HDR_LEN(iph) (iph->ihl << 2)
  #define TCP_PAYLOAD_LENGTH(iph, tcph) \
  	(ntohs(iph->tot_len) - IP_HDR_LEN(iph) - TCP_HDR_LEN(tcph))
  
  #define IPH_LEN_WO_OPTIONS 5
  #define TCPH_LEN_WO_OPTIONS 5
  #define TCPH_LEN_W_TIMESTAMP 8
  
  #define LRO_MAX_PG_HLEN 64
  
  #define LRO_INC_STATS(lro_mgr, attr) { lro_mgr->stats.attr++; }
  
  /*
   * Basic tcp checks whether packet is suitable for LRO
   */
b71d1d426   Eric Dumazet   inet: constify ip...
53
54
  static int lro_tcp_ip_check(const struct iphdr *iph, const struct tcphdr *tcph,
  			    int len, const struct net_lro_desc *lro_desc)
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
55
56
57
58
59
60
61
62
63
64
  {
          /* check ip header: don't aggregate padded frames */
  	if (ntohs(iph->tot_len) != len)
  		return -1;
  
  	if (TCP_PAYLOAD_LENGTH(iph, tcph) == 0)
  		return -1;
  
  	if (iph->ihl != IPH_LEN_WO_OPTIONS)
  		return -1;
9d4fb27db   Joe Perches   net/ipv4: Move &&...
65
66
  	if (tcph->cwr || tcph->ece || tcph->urg || !tcph->ack ||
  	    tcph->rst || tcph->syn || tcph->fin)
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
67
68
69
70
  		return -1;
  
  	if (INET_ECN_is_ce(ipv4_get_dsfield(iph)))
  		return -1;
9d4fb27db   Joe Perches   net/ipv4: Move &&...
71
72
  	if (tcph->doff != TCPH_LEN_WO_OPTIONS &&
  	    tcph->doff != TCPH_LEN_W_TIMESTAMP)
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
73
74
75
76
  		return -1;
  
  	/* check tcp options (only timestamp allowed) */
  	if (tcph->doff == TCPH_LEN_W_TIMESTAMP) {
9df7c98a0   Al Viro   inet_lro: trivial...
77
  		__be32 *topt = (__be32 *)(tcph + 1);
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
  
  		if (*topt != htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16)
  				   | (TCPOPT_TIMESTAMP << 8)
  				   | TCPOLEN_TIMESTAMP))
  			return -1;
  
  		/* timestamp should be in right order */
  		topt++;
  		if (lro_desc && after(ntohl(lro_desc->tcp_rcv_tsval),
  				      ntohl(*topt)))
  			return -1;
  
  		/* timestamp reply should not be zero */
  		topt++;
  		if (*topt == 0)
  			return -1;
  	}
  
  	return 0;
  }
  
  static void lro_update_tcp_ip_header(struct net_lro_desc *lro_desc)
  {
  	struct iphdr *iph = lro_desc->iph;
  	struct tcphdr *tcph = lro_desc->tcph;
9df7c98a0   Al Viro   inet_lro: trivial...
103
  	__be32 *p;
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
104
105
106
107
108
109
  	__wsum tcp_hdr_csum;
  
  	tcph->ack_seq = lro_desc->tcp_ack;
  	tcph->window = lro_desc->tcp_window;
  
  	if (lro_desc->tcp_saw_tstamp) {
9df7c98a0   Al Viro   inet_lro: trivial...
110
  		p = (__be32 *)(tcph + 1);
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
111
112
113
114
115
116
117
118
119
  		*(p+2) = lro_desc->tcp_rcv_tsecr;
  	}
  
  	iph->tot_len = htons(lro_desc->ip_tot_len);
  
  	iph->check = 0;
  	iph->check = ip_fast_csum((u8 *)lro_desc->iph, iph->ihl);
  
  	tcph->check = 0;
07f0757a6   Joe Perches   include/net net/ ...
120
  	tcp_hdr_csum = csum_partial(tcph, TCP_HDR_LEN(tcph), 0);
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
121
122
123
124
125
126
127
128
129
130
131
132
133
134
  	lro_desc->data_csum = csum_add(lro_desc->data_csum, tcp_hdr_csum);
  	tcph->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
  					lro_desc->ip_tot_len -
  					IP_HDR_LEN(iph), IPPROTO_TCP,
  					lro_desc->data_csum);
  }
  
  static __wsum lro_tcp_data_csum(struct iphdr *iph, struct tcphdr *tcph, int len)
  {
  	__wsum tcp_csum;
  	__wsum tcp_hdr_csum;
  	__wsum tcp_ps_hdr_csum;
  
  	tcp_csum = ~csum_unfold(tcph->check);
07f0757a6   Joe Perches   include/net net/ ...
135
  	tcp_hdr_csum = csum_partial(tcph, TCP_HDR_LEN(tcph), tcp_csum);
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
136
137
138
139
140
141
142
143
144
145
  
  	tcp_ps_hdr_csum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
  					     len + TCP_HDR_LEN(tcph),
  					     IPPROTO_TCP, 0);
  
  	return csum_sub(csum_sub(tcp_csum, tcp_hdr_csum),
  			tcp_ps_hdr_csum);
  }
  
  static void lro_init_desc(struct net_lro_desc *lro_desc, struct sk_buff *skb,
9fea03302   Jiri Pirko   lro: do vlan cleanup
146
  			  struct iphdr *iph, struct tcphdr *tcph)
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
147
148
  {
  	int nr_frags;
9df7c98a0   Al Viro   inet_lro: trivial...
149
  	__be32 *ptr;
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
150
151
152
153
154
155
156
157
  	u32 tcp_data_len = TCP_PAYLOAD_LENGTH(iph, tcph);
  
  	nr_frags = skb_shinfo(skb)->nr_frags;
  	lro_desc->parent = skb;
  	lro_desc->next_frag = &(skb_shinfo(skb)->frags[nr_frags]);
  	lro_desc->iph = iph;
  	lro_desc->tcph = tcph;
  	lro_desc->tcp_next_seq = ntohl(tcph->seq) + tcp_data_len;
f53f4137b   Al Viro   fix endianness bu...
158
  	lro_desc->tcp_ack = tcph->ack_seq;
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
159
160
161
162
163
164
  	lro_desc->tcp_window = tcph->window;
  
  	lro_desc->pkt_aggr_cnt = 1;
  	lro_desc->ip_tot_len = ntohs(iph->tot_len);
  
  	if (tcph->doff == 8) {
9df7c98a0   Al Viro   inet_lro: trivial...
165
  		ptr = (__be32 *)(tcph+1);
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
166
167
168
169
170
171
  		lro_desc->tcp_saw_tstamp = 1;
  		lro_desc->tcp_rcv_tsval = *(ptr+1);
  		lro_desc->tcp_rcv_tsecr = *(ptr+2);
  	}
  
  	lro_desc->mss = tcp_data_len;
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
  	lro_desc->active = 1;
  
  	lro_desc->data_csum = lro_tcp_data_csum(iph, tcph,
  						tcp_data_len);
  }
  
  static inline void lro_clear_desc(struct net_lro_desc *lro_desc)
  {
  	memset(lro_desc, 0, sizeof(struct net_lro_desc));
  }
  
  static void lro_add_common(struct net_lro_desc *lro_desc, struct iphdr *iph,
  			   struct tcphdr *tcph, int tcp_data_len)
  {
  	struct sk_buff *parent = lro_desc->parent;
9df7c98a0   Al Viro   inet_lro: trivial...
187
  	__be32 *topt;
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
188
189
190
191
192
193
194
195
196
  
  	lro_desc->pkt_aggr_cnt++;
  	lro_desc->ip_tot_len += tcp_data_len;
  	lro_desc->tcp_next_seq += tcp_data_len;
  	lro_desc->tcp_window = tcph->window;
  	lro_desc->tcp_ack = tcph->ack_seq;
  
  	/* don't update tcp_rcv_tsval, would not work with PAWS */
  	if (lro_desc->tcp_saw_tstamp) {
9df7c98a0   Al Viro   inet_lro: trivial...
197
  		topt = (__be32 *) (tcph + 1);
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
  		lro_desc->tcp_rcv_tsecr = *(topt + 2);
  	}
  
  	lro_desc->data_csum = csum_block_add(lro_desc->data_csum,
  					     lro_tcp_data_csum(iph, tcph,
  							       tcp_data_len),
  					     parent->len);
  
  	parent->len += tcp_data_len;
  	parent->data_len += tcp_data_len;
  	if (tcp_data_len > lro_desc->mss)
  		lro_desc->mss = tcp_data_len;
  }
  
  static void lro_add_packet(struct net_lro_desc *lro_desc, struct sk_buff *skb,
  			   struct iphdr *iph, struct tcphdr *tcph)
  {
  	struct sk_buff *parent = lro_desc->parent;
  	int tcp_data_len = TCP_PAYLOAD_LENGTH(iph, tcph);
  
  	lro_add_common(lro_desc, iph, tcph, tcp_data_len);
  
  	skb_pull(skb, (skb->len - tcp_data_len));
  	parent->truesize += skb->truesize;
  
  	if (lro_desc->last_skb)
  		lro_desc->last_skb->next = skb;
  	else
  		skb_shinfo(parent)->frag_list = skb;
  
  	lro_desc->last_skb = skb;
  }
  
  static void lro_add_frags(struct net_lro_desc *lro_desc,
  			  int len, int hlen, int truesize,
  			  struct skb_frag_struct *skb_frags,
  			  struct iphdr *iph, struct tcphdr *tcph)
  {
  	struct sk_buff *skb = lro_desc->parent;
  	int tcp_data_len = TCP_PAYLOAD_LENGTH(iph, tcph);
  
  	lro_add_common(lro_desc, iph, tcph, tcp_data_len);
  
  	skb->truesize += truesize;
  
  	skb_frags[0].page_offset += hlen;
9e903e085   Eric Dumazet   net: add skb frag...
244
  	skb_frag_size_sub(&skb_frags[0], hlen);
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
245
246
247
  
  	while (tcp_data_len > 0) {
  		*(lro_desc->next_frag) = *skb_frags;
9e903e085   Eric Dumazet   net: add skb frag...
248
  		tcp_data_len -= skb_frag_size(skb_frags);
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
249
250
251
252
253
254
255
256
257
258
  		lro_desc->next_frag++;
  		skb_frags++;
  		skb_shinfo(skb)->nr_frags++;
  	}
  }
  
  static int lro_check_tcp_conn(struct net_lro_desc *lro_desc,
  			      struct iphdr *iph,
  			      struct tcphdr *tcph)
  {
9d4fb27db   Joe Perches   net/ipv4: Move &&...
259
260
261
262
  	if ((lro_desc->iph->saddr != iph->saddr) ||
  	    (lro_desc->iph->daddr != iph->daddr) ||
  	    (lro_desc->tcph->source != tcph->source) ||
  	    (lro_desc->tcph->dest != tcph->dest))
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
  		return -1;
  	return 0;
  }
  
  static struct net_lro_desc *lro_get_desc(struct net_lro_mgr *lro_mgr,
  					 struct net_lro_desc *lro_arr,
  					 struct iphdr *iph,
  					 struct tcphdr *tcph)
  {
  	struct net_lro_desc *lro_desc = NULL;
  	struct net_lro_desc *tmp;
  	int max_desc = lro_mgr->max_desc;
  	int i;
  
  	for (i = 0; i < max_desc; i++) {
  		tmp = &lro_arr[i];
  		if (tmp->active)
  			if (!lro_check_tcp_conn(tmp, iph, tcph)) {
  				lro_desc = tmp;
  				goto out;
  			}
  	}
  
  	for (i = 0; i < max_desc; i++) {
  		if (!lro_arr[i].active) {
  			lro_desc = &lro_arr[i];
  			goto out;
  		}
  	}
  
  	LRO_INC_STATS(lro_mgr, no_desc);
  out:
  	return lro_desc;
  }
  
  static void lro_flush(struct net_lro_mgr *lro_mgr,
  		      struct net_lro_desc *lro_desc)
  {
  	if (lro_desc->pkt_aggr_cnt > 1)
  		lro_update_tcp_ip_header(lro_desc);
  
  	skb_shinfo(lro_desc->parent)->gso_size = lro_desc->mss;
9fea03302   Jiri Pirko   lro: do vlan cleanup
305
306
307
308
  	if (lro_mgr->features & LRO_F_NAPI)
  		netif_receive_skb(lro_desc->parent);
  	else
  		netif_rx(lro_desc->parent);
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
309
310
311
312
313
314
  
  	LRO_INC_STATS(lro_mgr, flushed);
  	lro_clear_desc(lro_desc);
  }
  
  static int __lro_proc_skb(struct net_lro_mgr *lro_mgr, struct sk_buff *skb,
9fea03302   Jiri Pirko   lro: do vlan cleanup
315
  			  void *priv)
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
316
317
318
319
320
321
  {
  	struct net_lro_desc *lro_desc;
  	struct iphdr *iph;
  	struct tcphdr *tcph;
  	u64 flags;
  	int vlan_hdr_len = 0;
9d4fb27db   Joe Perches   net/ipv4: Move &&...
322
323
324
  	if (!lro_mgr->get_skb_header ||
  	    lro_mgr->get_skb_header(skb, (void *)&iph, (void *)&tcph,
  				    &flags, priv))
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
325
326
327
328
329
330
331
332
  		goto out;
  
  	if (!(flags & LRO_IPV4) || !(flags & LRO_TCP))
  		goto out;
  
  	lro_desc = lro_get_desc(lro_mgr, lro_mgr->lro_arr, iph, tcph);
  	if (!lro_desc)
  		goto out;
9d4fb27db   Joe Perches   net/ipv4: Move &&...
333
334
  	if ((skb->protocol == htons(ETH_P_8021Q)) &&
  	    !(lro_mgr->features & LRO_F_EXTRACT_VLAN_ID))
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
335
336
337
338
339
340
341
  		vlan_hdr_len = VLAN_HLEN;
  
  	if (!lro_desc->active) { /* start new lro session */
  		if (lro_tcp_ip_check(iph, tcph, skb->len - vlan_hdr_len, NULL))
  			goto out;
  
  		skb->ip_summed = lro_mgr->ip_summed_aggr;
9fea03302   Jiri Pirko   lro: do vlan cleanup
342
  		lro_init_desc(lro_desc, skb, iph, tcph);
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
  		LRO_INC_STATS(lro_mgr, aggregated);
  		return 0;
  	}
  
  	if (lro_desc->tcp_next_seq != ntohl(tcph->seq))
  		goto out2;
  
  	if (lro_tcp_ip_check(iph, tcph, skb->len, lro_desc))
  		goto out2;
  
  	lro_add_packet(lro_desc, skb, iph, tcph);
  	LRO_INC_STATS(lro_mgr, aggregated);
  
  	if ((lro_desc->pkt_aggr_cnt >= lro_mgr->max_aggr) ||
  	    lro_desc->parent->len > (0xFFFF - lro_mgr->dev->mtu))
  		lro_flush(lro_mgr, lro_desc);
  
  	return 0;
  
  out2: /* send aggregated SKBs to stack */
  	lro_flush(lro_mgr, lro_desc);
251a4b320   Eli Cohen   net/inet_lro: rem...
364
  out:
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
  	return 1;
  }
  
  
  static struct sk_buff *lro_gen_skb(struct net_lro_mgr *lro_mgr,
  				   struct skb_frag_struct *frags,
  				   int len, int true_size,
  				   void *mac_hdr,
  				   int hlen, __wsum sum,
  				   u32 ip_summed)
  {
  	struct sk_buff *skb;
  	struct skb_frag_struct *skb_frags;
  	int data_len = len;
  	int hdr_len = min(len, hlen);
621544eb8   Andrew Gallatin   [LRO]: fix lro_ge...
380
  	skb = netdev_alloc_skb(lro_mgr->dev, hlen + lro_mgr->frag_align_pad);
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
381
382
  	if (!skb)
  		return NULL;
621544eb8   Andrew Gallatin   [LRO]: fix lro_ge...
383
  	skb_reserve(skb, lro_mgr->frag_align_pad);
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
384
385
386
387
388
389
390
391
392
393
  	skb->len = len;
  	skb->data_len = len - hdr_len;
  	skb->truesize += true_size;
  	skb->tail += hdr_len;
  
  	memcpy(skb->data, mac_hdr, hdr_len);
  
  	skb_frags = skb_shinfo(skb)->frags;
  	while (data_len > 0) {
  		*skb_frags = *frags;
9e903e085   Eric Dumazet   net: add skb frag...
394
  		data_len -= skb_frag_size(frags);
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
395
396
397
398
399
400
  		skb_frags++;
  		frags++;
  		skb_shinfo(skb)->nr_frags++;
  	}
  
  	skb_shinfo(skb)->frags[0].page_offset += hdr_len;
9e903e085   Eric Dumazet   net: add skb frag...
401
  	skb_frag_size_sub(&skb_shinfo(skb)->frags[0], hdr_len);
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
402
403
404
405
406
407
408
409
410
411
  
  	skb->ip_summed = ip_summed;
  	skb->csum = sum;
  	skb->protocol = eth_type_trans(skb, lro_mgr->dev);
  	return skb;
  }
  
  static struct sk_buff *__lro_proc_segment(struct net_lro_mgr *lro_mgr,
  					  struct skb_frag_struct *frags,
  					  int len, int true_size,
9fea03302   Jiri Pirko   lro: do vlan cleanup
412
  					  void *priv, __wsum sum)
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
413
414
415
416
417
418
419
420
421
422
  {
  	struct net_lro_desc *lro_desc;
  	struct iphdr *iph;
  	struct tcphdr *tcph;
  	struct sk_buff *skb;
  	u64 flags;
  	void *mac_hdr;
  	int mac_hdr_len;
  	int hdr_len = LRO_MAX_PG_HLEN;
  	int vlan_hdr_len = 0;
9d4fb27db   Joe Perches   net/ipv4: Move &&...
423
424
425
  	if (!lro_mgr->get_frag_header ||
  	    lro_mgr->get_frag_header(frags, (void *)&mac_hdr, (void *)&iph,
  				     (void *)&tcph, &flags, priv)) {
aff65da0f   Ian Campbell   net: ipv4: conver...
426
  		mac_hdr = skb_frag_address(frags);
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
  		goto out1;
  	}
  
  	if (!(flags & LRO_IPV4) || !(flags & LRO_TCP))
  		goto out1;
  
  	hdr_len = (int)((void *)(tcph) + TCP_HDR_LEN(tcph) - mac_hdr);
  	mac_hdr_len = (int)((void *)(iph) - mac_hdr);
  
  	lro_desc = lro_get_desc(lro_mgr, lro_mgr->lro_arr, iph, tcph);
  	if (!lro_desc)
  		goto out1;
  
  	if (!lro_desc->active) { /* start new lro session */
  		if (lro_tcp_ip_check(iph, tcph, len - mac_hdr_len, NULL))
  			goto out1;
  
  		skb = lro_gen_skb(lro_mgr, frags, len, true_size, mac_hdr,
  				  hdr_len, 0, lro_mgr->ip_summed_aggr);
  		if (!skb)
  			goto out;
9d4fb27db   Joe Perches   net/ipv4: Move &&...
448
449
  		if ((skb->protocol == htons(ETH_P_8021Q)) &&
  		    !(lro_mgr->features & LRO_F_EXTRACT_VLAN_ID))
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
450
451
452
453
454
  			vlan_hdr_len = VLAN_HLEN;
  
  		iph = (void *)(skb->data + vlan_hdr_len);
  		tcph = (void *)((u8 *)skb->data + vlan_hdr_len
  				+ IP_HDR_LEN(iph));
9fea03302   Jiri Pirko   lro: do vlan cleanup
455
  		lro_init_desc(lro_desc, skb, iph, tcph);
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
456
  		LRO_INC_STATS(lro_mgr, aggregated);
cfcabdcc2   Stephen Hemminger   [NET]: sparse war...
457
  		return NULL;
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
  	}
  
  	if (lro_desc->tcp_next_seq != ntohl(tcph->seq))
  		goto out2;
  
  	if (lro_tcp_ip_check(iph, tcph, len - mac_hdr_len, lro_desc))
  		goto out2;
  
  	lro_add_frags(lro_desc, len, hdr_len, true_size, frags, iph, tcph);
  	LRO_INC_STATS(lro_mgr, aggregated);
  
  	if ((skb_shinfo(lro_desc->parent)->nr_frags >= lro_mgr->max_aggr) ||
  	    lro_desc->parent->len > (0xFFFF - lro_mgr->dev->mtu))
  		lro_flush(lro_mgr, lro_desc);
  
  	return NULL;
  
  out2: /* send aggregated packets to the stack */
  	lro_flush(lro_mgr, lro_desc);
  
  out1:  /* Original packet has to be posted to the stack */
  	skb = lro_gen_skb(lro_mgr, frags, len, true_size, mac_hdr,
  			  hdr_len, sum, lro_mgr->ip_summed);
  out:
  	return skb;
  }
  
  void lro_receive_skb(struct net_lro_mgr *lro_mgr,
  		     struct sk_buff *skb,
  		     void *priv)
  {
9fea03302   Jiri Pirko   lro: do vlan cleanup
489
  	if (__lro_proc_skb(lro_mgr, skb, priv)) {
877364e60   Brice Goglin   [LRO] Fix lro_mgr...
490
  		if (lro_mgr->features & LRO_F_NAPI)
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
491
492
493
494
495
496
  			netif_receive_skb(skb);
  		else
  			netif_rx(skb);
  	}
  }
  EXPORT_SYMBOL(lro_receive_skb);
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
497
498
499
500
501
  void lro_receive_frags(struct net_lro_mgr *lro_mgr,
  		       struct skb_frag_struct *frags,
  		       int len, int true_size, void *priv, __wsum sum)
  {
  	struct sk_buff *skb;
9fea03302   Jiri Pirko   lro: do vlan cleanup
502
  	skb = __lro_proc_segment(lro_mgr, frags, len, true_size, priv, sum);
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
503
504
  	if (!skb)
  		return;
877364e60   Brice Goglin   [LRO] Fix lro_mgr...
505
  	if (lro_mgr->features & LRO_F_NAPI)
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
506
507
508
509
510
  		netif_receive_skb(skb);
  	else
  		netif_rx(skb);
  }
  EXPORT_SYMBOL(lro_receive_frags);
71c87e0ce   Jan-Bernd Themann   [NET]: Generic La...
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
  void lro_flush_all(struct net_lro_mgr *lro_mgr)
  {
  	int i;
  	struct net_lro_desc *lro_desc = lro_mgr->lro_arr;
  
  	for (i = 0; i < lro_mgr->max_desc; i++) {
  		if (lro_desc[i].active)
  			lro_flush(lro_mgr, &lro_desc[i]);
  	}
  }
  EXPORT_SYMBOL(lro_flush_all);
  
  void lro_flush_pkt(struct net_lro_mgr *lro_mgr,
  		  struct iphdr *iph, struct tcphdr *tcph)
  {
  	struct net_lro_desc *lro_desc;
  
  	lro_desc = lro_get_desc(lro_mgr, lro_mgr->lro_arr, iph, tcph);
  	if (lro_desc->active)
  		lro_flush(lro_mgr, lro_desc);
  }
  EXPORT_SYMBOL(lro_flush_pkt);