en_frag.c revision 219820
1241675Suqs/*
2241675Suqs * Copyright (c) 2007 Mellanox Technologies. All rights reserved.
3241675Suqs *
4241675Suqs * This software is available to you under a choice of one of two
5241675Suqs * licenses.  You may choose to be licensed under the terms of the GNU
6241675Suqs * General Public License (GPL) Version 2, available from the file
7241675Suqs * COPYING in the main directory of this source tree, or the
8241675Suqs * OpenIB.org BSD license below:
9241675Suqs *
10241675Suqs *     Redistribution and use in source and binary forms, with or
11241675Suqs *     without modification, are permitted provided that the following
12241675Suqs *     conditions are met:
13241675Suqs *
14241675Suqs *      - Redistributions of source code must retain the above
15241675Suqs *        copyright notice, this list of conditions and the following
16241675Suqs *        disclaimer.
17241675Suqs *
18241675Suqs *      - Redistributions in binary form must reproduce the above
19241675Suqs *        copyright notice, this list of conditions and the following
20241675Suqs *        disclaimer in the documentation and/or other materials
21241675Suqs *        provided with the distribution.
22241675Suqs *
23241675Suqs * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24241675Suqs * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25241675Suqs * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26241675Suqs * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27241675Suqs * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28241675Suqs * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29241675Suqs * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30241675Suqs * SOFTWARE.
31241675Suqs *
32241675Suqs */
33241675Suqs
34241675Suqs#include "mlx4_en.h"
35241675Suqs
36241675Suqs#include <net/ethernet.h>
37241675Suqs#include <netinet/ip.h>
38241675Suqs#include <machine/in_cksum.h>
39241675Suqs
40241675Suqsstatic struct mlx4_en_ipfrag *find_session(struct mlx4_en_rx_ring *ring,
41241675Suqs					   struct ip *iph)
42241675Suqs{
43241675Suqs	struct mlx4_en_ipfrag *session;
44241675Suqs	int i;
45241675Suqs
46241675Suqs	for (i = 0; i < MLX4_EN_NUM_IPFRAG_SESSIONS; i++) {
47241675Suqs		session = &ring->ipfrag[i];
48241675Suqs		if (session->fragments == NULL)
49241675Suqs			continue;
50241675Suqs		if (session->daddr == iph->ip_dst.s_addr &&
51241675Suqs		    session->saddr == iph->ip_src.s_addr &&
52241675Suqs		    session->id == iph->ip_id &&
53241675Suqs		    session->protocol == iph->ip_p) {
54241675Suqs			return session;
55241675Suqs		}
56241675Suqs	}
57241675Suqs	return NULL;
58241675Suqs}
59241675Suqs
60241675Suqsstatic struct mlx4_en_ipfrag *start_session(struct mlx4_en_rx_ring *ring,
61241675Suqs					    struct ip *iph)
62241675Suqs{
63241675Suqs	struct mlx4_en_ipfrag *session;
64241675Suqs	int index = -1;
65241675Suqs	int i;
66241675Suqs
67241675Suqs	for (i = 0; i < MLX4_EN_NUM_IPFRAG_SESSIONS; i++) {
68241675Suqs		if (ring->ipfrag[i].fragments == NULL) {
69241675Suqs			index = i;
70241675Suqs			break;
71241675Suqs		}
72241675Suqs	}
73241675Suqs	if (index < 0)
74241675Suqs		return NULL;
75241675Suqs
76241675Suqs	session = &ring->ipfrag[index];
77241675Suqs
78241675Suqs	return session;
79241675Suqs}
80241675Suqs
81241675Suqs
82241675Suqsstatic void flush_session(struct mlx4_en_priv *priv,
83241675Suqs			  struct mlx4_en_ipfrag *session,
84241675Suqs			  u16 more)
85241675Suqs{
86241675Suqs	struct mbuf *mb = session->fragments;
87241675Suqs	struct ip *iph = mb->m_pkthdr.header;
88241675Suqs	struct net_device *dev = mb->m_pkthdr.rcvif;
89241675Suqs
90241675Suqs	/* Update IP length and checksum */
91241675Suqs	iph->ip_len = htons(session->total_len);
92241675Suqs	iph->ip_off = htons(more | (session->offset >> 3));
93241675Suqs	iph->ip_sum = 0;
94241675Suqs	iph->ip_sum = in_cksum_skip(mb, iph->ip_hl * 4,
95241675Suqs	    (char *)iph - mb->m_data);
96241675Suqs
97241675Suqs	dev->if_input(dev, mb);
98241675Suqs	session->fragments = NULL;
99241675Suqs	session->last = NULL;
100241675Suqs}
101241675Suqs
102241675Suqs
103241675Suqsstatic inline void frag_append(struct mlx4_en_priv *priv,
104241675Suqs			       struct mlx4_en_ipfrag *session,
105241675Suqs			       struct mbuf *mb,
106241675Suqs			       unsigned int data_len)
107241675Suqs{
108241675Suqs	struct mbuf *parent = session->fragments;
109241675Suqs
110241675Suqs	/* Update mb bookkeeping */
111241675Suqs	parent->m_pkthdr.len += data_len;
112241675Suqs	session->total_len += data_len;
113241675Suqs
114241675Suqs	m_adj(mb, mb->m_pkthdr.len - data_len);
115241675Suqs
116241675Suqs	session->last->m_next = mb;
117241675Suqs	for (; mb->m_next != NULL; mb = mb->m_next);
118241675Suqs	session->last = mb;
119241675Suqs}
120241675Suqs
121241675Suqsint mlx4_en_rx_frags(struct mlx4_en_priv *priv, struct mlx4_en_rx_ring *ring,
122241675Suqs		     struct mbuf *mb, struct mlx4_cqe *cqe)
123241675Suqs{
124241675Suqs	struct mlx4_en_ipfrag *session;
125241675Suqs	struct ip *iph;
126241675Suqs	u16 ip_len;
127241675Suqs	u16 ip_hlen;
128241675Suqs	int data_len;
129241675Suqs	u16 offset;
130241675Suqs
131241675Suqs	iph = (struct ip *)(mtod(mb, char *) + ETHER_HDR_LEN);
132241675Suqs	mb->m_pkthdr.header = iph;
133241675Suqs	ip_len = ntohs(iph->ip_len);
134241675Suqs	ip_hlen = iph->ip_hl * 4;
135241675Suqs	data_len = ip_len - ip_hlen;
136241675Suqs	offset = ntohs(iph->ip_off);
137241675Suqs	offset &= IP_OFFMASK;
138241675Suqs	offset <<= 3;
139241675Suqs
140241675Suqs	session = find_session(ring, iph);
141241675Suqs	if (unlikely(in_cksum_skip(mb, ip_hlen, (char *)iph - mb->m_data))) {
142241675Suqs		if (session)
143241675Suqs			flush_session(priv, session, IP_MF);
144241675Suqs		return -EINVAL;
145241675Suqs	}
146241675Suqs	if (session) {
147241675Suqs		if (unlikely(session->offset + session->total_len !=
148241675Suqs		    offset + ip_hlen ||
149241675Suqs		    session->total_len + mb->m_pkthdr.len > 65536)) {
150241675Suqs			flush_session(priv, session, IP_MF);
151241675Suqs			goto new_session;
152241675Suqs		}
153241675Suqs		frag_append(priv, session, mb, data_len);
154241675Suqs	} else {
155241675Suqsnew_session:
156241675Suqs		session = start_session(ring, iph);
157241675Suqs		if (unlikely(!session))
158241675Suqs			return -ENOSPC;
159241675Suqs
160241675Suqs		session->fragments = mb;
161241675Suqs		session->daddr = iph->ip_dst.s_addr;
162241675Suqs		session->saddr = iph->ip_src.s_addr;
163241675Suqs		session->id = iph->ip_id;
164241675Suqs		session->protocol = iph->ip_p;
165241675Suqs		session->total_len = ip_len;
166241675Suqs		session->offset = offset;
167241675Suqs		for (; mb->m_next != NULL; mb = mb->m_next);
168241675Suqs		session->last = mb;
169241675Suqs	}
170241675Suqs	if (!(ntohs(iph->ip_off) & IP_MF))
171241675Suqs		flush_session(priv, session, 0);
172241675Suqs
173241675Suqs	return 0;
174241675Suqs}
175241675Suqs
176241675Suqs
177241675Suqsvoid mlx4_en_flush_frags(struct mlx4_en_priv *priv,
178241675Suqs			 struct mlx4_en_rx_ring *ring)
179241675Suqs{
180241675Suqs	struct mlx4_en_ipfrag *session;
181241675Suqs	int i;
182241675Suqs
183241675Suqs	for (i = 0; i < MLX4_EN_NUM_IPFRAG_SESSIONS; i++) {
184241675Suqs		session = &ring->ipfrag[i];
185241675Suqs		if (session->fragments)
186241675Suqs			flush_session(priv, session, IP_MF);
187241675Suqs	}
188241675Suqs}
189241675Suqs