]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/ofed/drivers/net/mlx4/en_frag.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / ofed / drivers / net / mlx4 / en_frag.c
1 /*
2  * Copyright (c) 2007 Mellanox Technologies. All rights reserved.
3  *
4  * This software is available to you under a choice of one of two
5  * licenses.  You may choose to be licensed under the terms of the GNU
6  * General Public License (GPL) Version 2, available from the file
7  * COPYING in the main directory of this source tree, or the
8  * OpenIB.org BSD license below:
9  *
10  *     Redistribution and use in source and binary forms, with or
11  *     without modification, are permitted provided that the following
12  *     conditions are met:
13  *
14  *      - Redistributions of source code must retain the above
15  *        copyright notice, this list of conditions and the following
16  *        disclaimer.
17  *
18  *      - Redistributions in binary form must reproduce the above
19  *        copyright notice, this list of conditions and the following
20  *        disclaimer in the documentation and/or other materials
21  *        provided with the distribution.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30  * SOFTWARE.
31  *
32  */
33
34 #include "opt_inet.h"
35 #include "mlx4_en.h"
36
37 #ifdef INET
38
39 #include <net/ethernet.h>
40 #include <netinet/ip.h>
41 #include <machine/in_cksum.h>
42
43 static struct mlx4_en_ipfrag *find_session(struct mlx4_en_rx_ring *ring,
44                                            struct ip *iph)
45 {
46         struct mlx4_en_ipfrag *session;
47         int i;
48
49         for (i = 0; i < MLX4_EN_NUM_IPFRAG_SESSIONS; i++) {
50                 session = &ring->ipfrag[i];
51                 if (session->fragments == NULL)
52                         continue;
53                 if (session->daddr == iph->ip_dst.s_addr &&
54                     session->saddr == iph->ip_src.s_addr &&
55                     session->id == iph->ip_id &&
56                     session->protocol == iph->ip_p) {
57                         return session;
58                 }
59         }
60         return NULL;
61 }
62
63 static struct mlx4_en_ipfrag *start_session(struct mlx4_en_rx_ring *ring,
64                                             struct ip *iph)
65 {
66         struct mlx4_en_ipfrag *session;
67         int index = -1;
68         int i;
69
70         for (i = 0; i < MLX4_EN_NUM_IPFRAG_SESSIONS; i++) {
71                 if (ring->ipfrag[i].fragments == NULL) {
72                         index = i;
73                         break;
74                 }
75         }
76         if (index < 0)
77                 return NULL;
78
79         session = &ring->ipfrag[index];
80
81         return session;
82 }
83
84
85 static void flush_session(struct mlx4_en_priv *priv,
86                           struct mlx4_en_ipfrag *session,
87                           u16 more)
88 {
89         struct mbuf *mb = session->fragments;
90         struct ip *iph = mb->m_pkthdr.PH_loc.ptr;
91         struct net_device *dev = mb->m_pkthdr.rcvif;
92
93         /* Update IP length and checksum */
94         iph->ip_len = htons(session->total_len);
95         iph->ip_off = htons(more | (session->offset >> 3));
96         iph->ip_sum = 0;
97         iph->ip_sum = in_cksum_skip(mb, iph->ip_hl * 4,
98             (char *)iph - mb->m_data);
99
100         dev->if_input(dev, mb);
101         session->fragments = NULL;
102         session->last = NULL;
103 }
104
105
106 static inline void frag_append(struct mlx4_en_priv *priv,
107                                struct mlx4_en_ipfrag *session,
108                                struct mbuf *mb,
109                                unsigned int data_len)
110 {
111         struct mbuf *parent = session->fragments;
112
113         /* Update mb bookkeeping */
114         parent->m_pkthdr.len += data_len;
115         session->total_len += data_len;
116
117         m_adj(mb, mb->m_pkthdr.len - data_len);
118
119         session->last->m_next = mb;
120         for (; mb->m_next != NULL; mb = mb->m_next);
121         session->last = mb;
122 }
123
124 int mlx4_en_rx_frags(struct mlx4_en_priv *priv, struct mlx4_en_rx_ring *ring,
125                      struct mbuf *mb, struct mlx4_cqe *cqe)
126 {
127         struct mlx4_en_ipfrag *session;
128         struct ip *iph;
129         u16 ip_len;
130         u16 ip_hlen;
131         int data_len;
132         u16 offset;
133
134         iph = (struct ip *)(mtod(mb, char *) + ETHER_HDR_LEN);
135         mb->m_pkthdr.PH_loc.ptr = iph;
136         ip_len = ntohs(iph->ip_len);
137         ip_hlen = iph->ip_hl * 4;
138         data_len = ip_len - ip_hlen;
139         offset = ntohs(iph->ip_off);
140         offset &= IP_OFFMASK;
141         offset <<= 3;
142
143         session = find_session(ring, iph);
144         if (unlikely(in_cksum_skip(mb, ip_hlen, (char *)iph - mb->m_data))) {
145                 if (session)
146                         flush_session(priv, session, IP_MF);
147                 return -EINVAL;
148         }
149         if (session) {
150                 if (unlikely(session->offset + session->total_len !=
151                     offset + ip_hlen ||
152                     session->total_len + mb->m_pkthdr.len > 65536)) {
153                         flush_session(priv, session, IP_MF);
154                         goto new_session;
155                 }
156                 frag_append(priv, session, mb, data_len);
157         } else {
158 new_session:
159                 session = start_session(ring, iph);
160                 if (unlikely(!session))
161                         return -ENOSPC;
162
163                 session->fragments = mb;
164                 session->daddr = iph->ip_dst.s_addr;
165                 session->saddr = iph->ip_src.s_addr;
166                 session->id = iph->ip_id;
167                 session->protocol = iph->ip_p;
168                 session->total_len = ip_len;
169                 session->offset = offset;
170                 for (; mb->m_next != NULL; mb = mb->m_next);
171                 session->last = mb;
172         }
173         if (!(ntohs(iph->ip_off) & IP_MF))
174                 flush_session(priv, session, 0);
175
176         return 0;
177 }
178
179
180 void mlx4_en_flush_frags(struct mlx4_en_priv *priv,
181                          struct mlx4_en_rx_ring *ring)
182 {
183         struct mlx4_en_ipfrag *session;
184         int i;
185
186         for (i = 0; i < MLX4_EN_NUM_IPFRAG_SESSIONS; i++) {
187                 session = &ring->ipfrag[i];
188                 if (session->fragments)
189                         flush_session(priv, session, IP_MF);
190         }
191 }
192 #endif