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 43static 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 63static 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 85static 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 106static 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 124int 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 { 158new_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 180void 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 193