1219820Sjeff/* 2219820Sjeff * Copyright (c) 2007 Mellanox Technologies. All rights reserved. 3219820Sjeff * 4219820Sjeff * This software is available to you under a choice of one of two 5219820Sjeff * licenses. You may choose to be licensed under the terms of the GNU 6219820Sjeff * General Public License (GPL) Version 2, available from the file 7219820Sjeff * COPYING in the main directory of this source tree, or the 8219820Sjeff * OpenIB.org BSD license below: 9219820Sjeff * 10219820Sjeff * Redistribution and use in source and binary forms, with or 11219820Sjeff * without modification, are permitted provided that the following 12219820Sjeff * conditions are met: 13219820Sjeff * 14219820Sjeff * - Redistributions of source code must retain the above 15219820Sjeff * copyright notice, this list of conditions and the following 16219820Sjeff * disclaimer. 17219820Sjeff * 18219820Sjeff * - Redistributions in binary form must reproduce the above 19219820Sjeff * copyright notice, this list of conditions and the following 20219820Sjeff * disclaimer in the documentation and/or other materials 21219820Sjeff * provided with the distribution. 22219820Sjeff * 23219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 27219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 28219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 29219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30219820Sjeff * SOFTWARE. 31219820Sjeff * 32219820Sjeff */ 33219820Sjeff 34234183Sjhb#include "opt_inet.h" 35219820Sjeff#include "mlx4_en.h" 36219820Sjeff 37234183Sjhb#ifdef INET 38234183Sjhb 39219820Sjeff#include <net/ethernet.h> 40219820Sjeff#include <netinet/ip.h> 41219820Sjeff#include <machine/in_cksum.h> 42219820Sjeff 43219820Sjeffstatic struct mlx4_en_ipfrag *find_session(struct mlx4_en_rx_ring *ring, 44219820Sjeff struct ip *iph) 45219820Sjeff{ 46219820Sjeff struct mlx4_en_ipfrag *session; 47219820Sjeff int i; 48219820Sjeff 49219820Sjeff for (i = 0; i < MLX4_EN_NUM_IPFRAG_SESSIONS; i++) { 50219820Sjeff session = &ring->ipfrag[i]; 51219820Sjeff if (session->fragments == NULL) 52219820Sjeff continue; 53219820Sjeff if (session->daddr == iph->ip_dst.s_addr && 54219820Sjeff session->saddr == iph->ip_src.s_addr && 55219820Sjeff session->id == iph->ip_id && 56219820Sjeff session->protocol == iph->ip_p) { 57219820Sjeff return session; 58219820Sjeff } 59219820Sjeff } 60219820Sjeff return NULL; 61219820Sjeff} 62219820Sjeff 63219820Sjeffstatic struct mlx4_en_ipfrag *start_session(struct mlx4_en_rx_ring *ring, 64219820Sjeff struct ip *iph) 65219820Sjeff{ 66219820Sjeff struct mlx4_en_ipfrag *session; 67219820Sjeff int index = -1; 68219820Sjeff int i; 69219820Sjeff 70219820Sjeff for (i = 0; i < MLX4_EN_NUM_IPFRAG_SESSIONS; i++) { 71219820Sjeff if (ring->ipfrag[i].fragments == NULL) { 72219820Sjeff index = i; 73219820Sjeff break; 74219820Sjeff } 75219820Sjeff } 76219820Sjeff if (index < 0) 77219820Sjeff return NULL; 78219820Sjeff 79219820Sjeff session = &ring->ipfrag[index]; 80219820Sjeff 81219820Sjeff return session; 82219820Sjeff} 83219820Sjeff 84219820Sjeff 85219820Sjeffstatic void flush_session(struct mlx4_en_priv *priv, 86219820Sjeff struct mlx4_en_ipfrag *session, 87219820Sjeff u16 more) 88219820Sjeff{ 89219820Sjeff struct mbuf *mb = session->fragments; 90254832Sandre struct ip *iph = mb->m_pkthdr.PH_loc.ptr; 91219820Sjeff struct net_device *dev = mb->m_pkthdr.rcvif; 92219820Sjeff 93219820Sjeff /* Update IP length and checksum */ 94219820Sjeff iph->ip_len = htons(session->total_len); 95219820Sjeff iph->ip_off = htons(more | (session->offset >> 3)); 96219820Sjeff iph->ip_sum = 0; 97219820Sjeff iph->ip_sum = in_cksum_skip(mb, iph->ip_hl * 4, 98219820Sjeff (char *)iph - mb->m_data); 99219820Sjeff 100219820Sjeff dev->if_input(dev, mb); 101219820Sjeff session->fragments = NULL; 102219820Sjeff session->last = NULL; 103219820Sjeff} 104219820Sjeff 105219820Sjeff 106219820Sjeffstatic inline void frag_append(struct mlx4_en_priv *priv, 107219820Sjeff struct mlx4_en_ipfrag *session, 108219820Sjeff struct mbuf *mb, 109219820Sjeff unsigned int data_len) 110219820Sjeff{ 111219820Sjeff struct mbuf *parent = session->fragments; 112219820Sjeff 113219820Sjeff /* Update mb bookkeeping */ 114219820Sjeff parent->m_pkthdr.len += data_len; 115219820Sjeff session->total_len += data_len; 116219820Sjeff 117219820Sjeff m_adj(mb, mb->m_pkthdr.len - data_len); 118219820Sjeff 119219820Sjeff session->last->m_next = mb; 120219820Sjeff for (; mb->m_next != NULL; mb = mb->m_next); 121219820Sjeff session->last = mb; 122219820Sjeff} 123219820Sjeff 124219820Sjeffint mlx4_en_rx_frags(struct mlx4_en_priv *priv, struct mlx4_en_rx_ring *ring, 125219820Sjeff struct mbuf *mb, struct mlx4_cqe *cqe) 126219820Sjeff{ 127219820Sjeff struct mlx4_en_ipfrag *session; 128219820Sjeff struct ip *iph; 129219820Sjeff u16 ip_len; 130219820Sjeff u16 ip_hlen; 131219820Sjeff int data_len; 132219820Sjeff u16 offset; 133219820Sjeff 134219820Sjeff iph = (struct ip *)(mtod(mb, char *) + ETHER_HDR_LEN); 135254832Sandre mb->m_pkthdr.PH_loc.ptr = iph; 136219820Sjeff ip_len = ntohs(iph->ip_len); 137219820Sjeff ip_hlen = iph->ip_hl * 4; 138219820Sjeff data_len = ip_len - ip_hlen; 139219820Sjeff offset = ntohs(iph->ip_off); 140219820Sjeff offset &= IP_OFFMASK; 141219820Sjeff offset <<= 3; 142219820Sjeff 143219820Sjeff session = find_session(ring, iph); 144219820Sjeff if (unlikely(in_cksum_skip(mb, ip_hlen, (char *)iph - mb->m_data))) { 145219820Sjeff if (session) 146219820Sjeff flush_session(priv, session, IP_MF); 147219820Sjeff return -EINVAL; 148219820Sjeff } 149219820Sjeff if (session) { 150219820Sjeff if (unlikely(session->offset + session->total_len != 151219820Sjeff offset + ip_hlen || 152219820Sjeff session->total_len + mb->m_pkthdr.len > 65536)) { 153219820Sjeff flush_session(priv, session, IP_MF); 154219820Sjeff goto new_session; 155219820Sjeff } 156219820Sjeff frag_append(priv, session, mb, data_len); 157219820Sjeff } else { 158219820Sjeffnew_session: 159219820Sjeff session = start_session(ring, iph); 160219820Sjeff if (unlikely(!session)) 161219820Sjeff return -ENOSPC; 162219820Sjeff 163219820Sjeff session->fragments = mb; 164219820Sjeff session->daddr = iph->ip_dst.s_addr; 165219820Sjeff session->saddr = iph->ip_src.s_addr; 166219820Sjeff session->id = iph->ip_id; 167219820Sjeff session->protocol = iph->ip_p; 168219820Sjeff session->total_len = ip_len; 169219820Sjeff session->offset = offset; 170219820Sjeff for (; mb->m_next != NULL; mb = mb->m_next); 171219820Sjeff session->last = mb; 172219820Sjeff } 173219820Sjeff if (!(ntohs(iph->ip_off) & IP_MF)) 174219820Sjeff flush_session(priv, session, 0); 175219820Sjeff 176219820Sjeff return 0; 177219820Sjeff} 178219820Sjeff 179219820Sjeff 180219820Sjeffvoid mlx4_en_flush_frags(struct mlx4_en_priv *priv, 181219820Sjeff struct mlx4_en_rx_ring *ring) 182219820Sjeff{ 183219820Sjeff struct mlx4_en_ipfrag *session; 184219820Sjeff int i; 185219820Sjeff 186219820Sjeff for (i = 0; i < MLX4_EN_NUM_IPFRAG_SESSIONS; i++) { 187219820Sjeff session = &ring->ipfrag[i]; 188219820Sjeff if (session->fragments) 189219820Sjeff flush_session(priv, session, IP_MF); 190219820Sjeff } 191219820Sjeff} 192234183Sjhb#endif 193