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