en_frag.c revision 219820
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 "mlx4_en.h" 35 36#include <net/ethernet.h> 37#include <netinet/ip.h> 38#include <machine/in_cksum.h> 39 40static struct mlx4_en_ipfrag *find_session(struct mlx4_en_rx_ring *ring, 41 struct ip *iph) 42{ 43 struct mlx4_en_ipfrag *session; 44 int i; 45 46 for (i = 0; i < MLX4_EN_NUM_IPFRAG_SESSIONS; i++) { 47 session = &ring->ipfrag[i]; 48 if (session->fragments == NULL) 49 continue; 50 if (session->daddr == iph->ip_dst.s_addr && 51 session->saddr == iph->ip_src.s_addr && 52 session->id == iph->ip_id && 53 session->protocol == iph->ip_p) { 54 return session; 55 } 56 } 57 return NULL; 58} 59 60static struct mlx4_en_ipfrag *start_session(struct mlx4_en_rx_ring *ring, 61 struct ip *iph) 62{ 63 struct mlx4_en_ipfrag *session; 64 int index = -1; 65 int i; 66 67 for (i = 0; i < MLX4_EN_NUM_IPFRAG_SESSIONS; i++) { 68 if (ring->ipfrag[i].fragments == NULL) { 69 index = i; 70 break; 71 } 72 } 73 if (index < 0) 74 return NULL; 75 76 session = &ring->ipfrag[index]; 77 78 return session; 79} 80 81 82static void flush_session(struct mlx4_en_priv *priv, 83 struct mlx4_en_ipfrag *session, 84 u16 more) 85{ 86 struct mbuf *mb = session->fragments; 87 struct ip *iph = mb->m_pkthdr.header; 88 struct net_device *dev = mb->m_pkthdr.rcvif; 89 90 /* Update IP length and checksum */ 91 iph->ip_len = htons(session->total_len); 92 iph->ip_off = htons(more | (session->offset >> 3)); 93 iph->ip_sum = 0; 94 iph->ip_sum = in_cksum_skip(mb, iph->ip_hl * 4, 95 (char *)iph - mb->m_data); 96 97 dev->if_input(dev, mb); 98 session->fragments = NULL; 99 session->last = NULL; 100} 101 102 103static inline void frag_append(struct mlx4_en_priv *priv, 104 struct mlx4_en_ipfrag *session, 105 struct mbuf *mb, 106 unsigned int data_len) 107{ 108 struct mbuf *parent = session->fragments; 109 110 /* Update mb bookkeeping */ 111 parent->m_pkthdr.len += data_len; 112 session->total_len += data_len; 113 114 m_adj(mb, mb->m_pkthdr.len - data_len); 115 116 session->last->m_next = mb; 117 for (; mb->m_next != NULL; mb = mb->m_next); 118 session->last = mb; 119} 120 121int mlx4_en_rx_frags(struct mlx4_en_priv *priv, struct mlx4_en_rx_ring *ring, 122 struct mbuf *mb, struct mlx4_cqe *cqe) 123{ 124 struct mlx4_en_ipfrag *session; 125 struct ip *iph; 126 u16 ip_len; 127 u16 ip_hlen; 128 int data_len; 129 u16 offset; 130 131 iph = (struct ip *)(mtod(mb, char *) + ETHER_HDR_LEN); 132 mb->m_pkthdr.header = iph; 133 ip_len = ntohs(iph->ip_len); 134 ip_hlen = iph->ip_hl * 4; 135 data_len = ip_len - ip_hlen; 136 offset = ntohs(iph->ip_off); 137 offset &= IP_OFFMASK; 138 offset <<= 3; 139 140 session = find_session(ring, iph); 141 if (unlikely(in_cksum_skip(mb, ip_hlen, (char *)iph - mb->m_data))) { 142 if (session) 143 flush_session(priv, session, IP_MF); 144 return -EINVAL; 145 } 146 if (session) { 147 if (unlikely(session->offset + session->total_len != 148 offset + ip_hlen || 149 session->total_len + mb->m_pkthdr.len > 65536)) { 150 flush_session(priv, session, IP_MF); 151 goto new_session; 152 } 153 frag_append(priv, session, mb, data_len); 154 } else { 155new_session: 156 session = start_session(ring, iph); 157 if (unlikely(!session)) 158 return -ENOSPC; 159 160 session->fragments = mb; 161 session->daddr = iph->ip_dst.s_addr; 162 session->saddr = iph->ip_src.s_addr; 163 session->id = iph->ip_id; 164 session->protocol = iph->ip_p; 165 session->total_len = ip_len; 166 session->offset = offset; 167 for (; mb->m_next != NULL; mb = mb->m_next); 168 session->last = mb; 169 } 170 if (!(ntohs(iph->ip_off) & IP_MF)) 171 flush_session(priv, session, 0); 172 173 return 0; 174} 175 176 177void mlx4_en_flush_frags(struct mlx4_en_priv *priv, 178 struct mlx4_en_rx_ring *ring) 179{ 180 struct mlx4_en_ipfrag *session; 181 int i; 182 183 for (i = 0; i < MLX4_EN_NUM_IPFRAG_SESSIONS; i++) { 184 session = &ring->ipfrag[i]; 185 if (session->fragments) 186 flush_session(priv, session, IP_MF); 187 } 188} 189