Commit 36b6f2cf authored by David S. Miller's avatar David S. Miller

Merge branch 'inet_lro-remove'

Ben Hutchings says:

====================
Remove the inet_lro library

The old inet_lro library has been deprecated ever since GRO was
introduced, but there are still a few drivers using it.  Convert
them to GRO and remove it.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 82aaf4fc 7bbf3cae
...@@ -2,7 +2,6 @@ config INFINIBAND_NES ...@@ -2,7 +2,6 @@ config INFINIBAND_NES
tristate "NetEffect RNIC Driver" tristate "NetEffect RNIC Driver"
depends on PCI && INET && INFINIBAND depends on PCI && INET && INFINIBAND
select LIBCRC32C select LIBCRC32C
select INET_LRO
---help--- ---help---
This is the RDMA Network Interface Card (RNIC) driver for This is the RDMA Network Interface Card (RNIC) driver for
NetEffect Ethernet Cluster Server Adapters. NetEffect Ethernet Cluster Server Adapters.
......
...@@ -35,18 +35,11 @@ ...@@ -35,18 +35,11 @@
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include <linux/inet_lro.h>
#include <linux/slab.h> #include <linux/slab.h>
#include "nes.h" #include "nes.h"
static unsigned int nes_lro_max_aggr = NES_LRO_MAX_AGGR;
module_param(nes_lro_max_aggr, uint, 0444);
MODULE_PARM_DESC(nes_lro_max_aggr, "NIC LRO max packet aggregation");
static int wide_ppm_offset; static int wide_ppm_offset;
module_param(wide_ppm_offset, int, 0644); module_param(wide_ppm_offset, int, 0644);
MODULE_PARM_DESC(wide_ppm_offset, "Increase CX4 interface clock ppm offset, 0=100ppm (default), 1=300ppm"); MODULE_PARM_DESC(wide_ppm_offset, "Increase CX4 interface clock ppm offset, 0=100ppm (default), 1=300ppm");
...@@ -1642,25 +1635,6 @@ static void nes_rq_wqes_timeout(unsigned long parm) ...@@ -1642,25 +1635,6 @@ static void nes_rq_wqes_timeout(unsigned long parm)
} }
static int nes_lro_get_skb_hdr(struct sk_buff *skb, void **iphdr,
void **tcph, u64 *hdr_flags, void *priv)
{
unsigned int ip_len;
struct iphdr *iph;
skb_reset_network_header(skb);
iph = ip_hdr(skb);
if (iph->protocol != IPPROTO_TCP)
return -1;
ip_len = ip_hdrlen(skb);
skb_set_transport_header(skb, ip_len);
*tcph = tcp_hdr(skb);
*hdr_flags = LRO_IPV4 | LRO_TCP;
*iphdr = iph;
return 0;
}
/** /**
* nes_init_nic_qp * nes_init_nic_qp
*/ */
...@@ -1895,14 +1869,6 @@ int nes_init_nic_qp(struct nes_device *nesdev, struct net_device *netdev) ...@@ -1895,14 +1869,6 @@ int nes_init_nic_qp(struct nes_device *nesdev, struct net_device *netdev)
return -ENOMEM; return -ENOMEM;
} }
nesvnic->lro_mgr.max_aggr = nes_lro_max_aggr;
nesvnic->lro_mgr.max_desc = NES_MAX_LRO_DESCRIPTORS;
nesvnic->lro_mgr.lro_arr = nesvnic->lro_desc;
nesvnic->lro_mgr.get_skb_header = nes_lro_get_skb_hdr;
nesvnic->lro_mgr.features = LRO_F_NAPI | LRO_F_EXTRACT_VLAN_ID;
nesvnic->lro_mgr.dev = netdev;
nesvnic->lro_mgr.ip_summed = CHECKSUM_UNNECESSARY;
nesvnic->lro_mgr.ip_summed_aggr = CHECKSUM_UNNECESSARY;
return 0; return 0;
} }
...@@ -2809,13 +2775,10 @@ void nes_nic_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq) ...@@ -2809,13 +2775,10 @@ void nes_nic_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq)
u16 pkt_type; u16 pkt_type;
u16 rqes_processed = 0; u16 rqes_processed = 0;
u8 sq_cqes = 0; u8 sq_cqes = 0;
u8 nes_use_lro = 0;
head = cq->cq_head; head = cq->cq_head;
cq_size = cq->cq_size; cq_size = cq->cq_size;
cq->cqes_pending = 1; cq->cqes_pending = 1;
if (nesvnic->netdev->features & NETIF_F_LRO)
nes_use_lro = 1;
do { do {
if (le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_NIC_CQE_MISC_IDX]) & if (le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_NIC_CQE_MISC_IDX]) &
NES_NIC_CQE_VALID) { NES_NIC_CQE_VALID) {
...@@ -2950,10 +2913,7 @@ void nes_nic_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq) ...@@ -2950,10 +2913,7 @@ void nes_nic_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq)
__vlan_hwaccel_put_tag(rx_skb, htons(ETH_P_8021Q), vlan_tag); __vlan_hwaccel_put_tag(rx_skb, htons(ETH_P_8021Q), vlan_tag);
} }
if (nes_use_lro) napi_gro_receive(&nesvnic->napi, rx_skb);
lro_receive_skb(&nesvnic->lro_mgr, rx_skb, NULL);
else
netif_receive_skb(rx_skb);
skip_rx_indicate0: skip_rx_indicate0:
; ;
...@@ -2984,8 +2944,6 @@ void nes_nic_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq) ...@@ -2984,8 +2944,6 @@ void nes_nic_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq)
} while (1); } while (1);
if (nes_use_lro)
lro_flush_all(&nesvnic->lro_mgr);
if (sq_cqes) { if (sq_cqes) {
barrier(); barrier();
/* restart the queue if it had been stopped */ /* restart the queue if it had been stopped */
......
...@@ -33,8 +33,6 @@ ...@@ -33,8 +33,6 @@
#ifndef __NES_HW_H #ifndef __NES_HW_H
#define __NES_HW_H #define __NES_HW_H
#include <linux/inet_lro.h>
#define NES_PHY_TYPE_CX4 1 #define NES_PHY_TYPE_CX4 1
#define NES_PHY_TYPE_1G 2 #define NES_PHY_TYPE_1G 2
#define NES_PHY_TYPE_ARGUS 4 #define NES_PHY_TYPE_ARGUS 4
...@@ -1049,8 +1047,6 @@ struct nes_hw_tune_timer { ...@@ -1049,8 +1047,6 @@ struct nes_hw_tune_timer {
#define NES_TIMER_ENABLE_LIMIT 4 #define NES_TIMER_ENABLE_LIMIT 4
#define NES_MAX_LINK_INTERRUPTS 128 #define NES_MAX_LINK_INTERRUPTS 128
#define NES_MAX_LINK_CHECK 200 #define NES_MAX_LINK_CHECK 200
#define NES_MAX_LRO_DESCRIPTORS 32
#define NES_LRO_MAX_AGGR 64
struct nes_adapter { struct nes_adapter {
u64 fw_ver; u64 fw_ver;
...@@ -1263,9 +1259,6 @@ struct nes_vnic { ...@@ -1263,9 +1259,6 @@ struct nes_vnic {
u8 next_qp_nic_index; u8 next_qp_nic_index;
u8 of_device_registered; u8 of_device_registered;
u8 rdma_enabled; u8 rdma_enabled;
u32 lro_max_aggr;
struct net_lro_mgr lro_mgr;
struct net_lro_desc lro_desc[NES_MAX_LRO_DESCRIPTORS];
struct timer_list event_timer; struct timer_list event_timer;
enum ib_event_type delayed_event; enum ib_event_type delayed_event;
enum ib_event_type last_dispatched_event; enum ib_event_type last_dispatched_event;
......
...@@ -1085,9 +1085,6 @@ static const char nes_ethtool_stringset[][ETH_GSTRING_LEN] = { ...@@ -1085,9 +1085,6 @@ static const char nes_ethtool_stringset[][ETH_GSTRING_LEN] = {
"Free 4Kpbls", "Free 4Kpbls",
"Free 256pbls", "Free 256pbls",
"Timer Inits", "Timer Inits",
"LRO aggregated",
"LRO flushed",
"LRO no_desc",
"PAU CreateQPs", "PAU CreateQPs",
"PAU DestroyQPs", "PAU DestroyQPs",
}; };
...@@ -1302,9 +1299,6 @@ static void nes_netdev_get_ethtool_stats(struct net_device *netdev, ...@@ -1302,9 +1299,6 @@ static void nes_netdev_get_ethtool_stats(struct net_device *netdev,
target_stat_values[++index] = nesadapter->free_4kpbl; target_stat_values[++index] = nesadapter->free_4kpbl;
target_stat_values[++index] = nesadapter->free_256pbl; target_stat_values[++index] = nesadapter->free_256pbl;
target_stat_values[++index] = int_mod_timer_init; target_stat_values[++index] = int_mod_timer_init;
target_stat_values[++index] = nesvnic->lro_mgr.stats.aggregated;
target_stat_values[++index] = nesvnic->lro_mgr.stats.flushed;
target_stat_values[++index] = nesvnic->lro_mgr.stats.no_desc;
target_stat_values[++index] = atomic_read(&pau_qps_created); target_stat_values[++index] = atomic_read(&pau_qps_created);
target_stat_values[++index] = atomic_read(&pau_qps_destroyed); target_stat_values[++index] = atomic_read(&pau_qps_destroyed);
} }
...@@ -1709,7 +1703,6 @@ struct net_device *nes_netdev_init(struct nes_device *nesdev, ...@@ -1709,7 +1703,6 @@ struct net_device *nes_netdev_init(struct nes_device *nesdev,
netdev->hw_features |= NETIF_F_TSO; netdev->hw_features |= NETIF_F_TSO;
netdev->features = netdev->hw_features | NETIF_F_HIGHDMA | NETIF_F_HW_VLAN_CTAG_TX; netdev->features = netdev->hw_features | NETIF_F_HIGHDMA | NETIF_F_HW_VLAN_CTAG_TX;
netdev->hw_features |= NETIF_F_LRO;
nes_debug(NES_DBG_INIT, "nesvnic = %p, reported features = 0x%lX, QPid = %d," nes_debug(NES_DBG_INIT, "nesvnic = %p, reported features = 0x%lX, QPid = %d,"
" nic_index = %d, logical_port = %d, mac_index = %d.\n", " nic_index = %d, logical_port = %d, mac_index = %d.\n",
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
config NET_VENDOR_PASEMI config NET_VENDOR_PASEMI
bool "PA Semi devices" bool "PA Semi devices"
default y default y
depends on PPC_PASEMI && PCI && INET depends on PPC_PASEMI && PCI
---help--- ---help---
If you have a network (Ethernet) card belonging to this class, say Y. If you have a network (Ethernet) card belonging to this class, say Y.
...@@ -18,9 +18,8 @@ if NET_VENDOR_PASEMI ...@@ -18,9 +18,8 @@ if NET_VENDOR_PASEMI
config PASEMI_MAC config PASEMI_MAC
tristate "PA Semi 1/10Gbit MAC" tristate "PA Semi 1/10Gbit MAC"
depends on PPC_PASEMI && PCI && INET depends on PPC_PASEMI && PCI
select PHYLIB select PHYLIB
select INET_LRO
---help--- ---help---
This driver supports the on-chip 1/10Gbit Ethernet controller on This driver supports the on-chip 1/10Gbit Ethernet controller on
PA Semi's PWRficient line of chips. PA Semi's PWRficient line of chips.
......
...@@ -30,9 +30,7 @@ ...@@ -30,9 +30,7 @@
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/tcp.h>
#include <net/checksum.h> #include <net/checksum.h>
#include <linux/inet_lro.h>
#include <linux/prefetch.h> #include <linux/prefetch.h>
#include <asm/irq.h> #include <asm/irq.h>
...@@ -52,12 +50,9 @@ ...@@ -52,12 +50,9 @@
* *
* - Multicast support * - Multicast support
* - Large MTU support * - Large MTU support
* - SW LRO
* - Multiqueue RX/TX * - Multiqueue RX/TX
*/ */
#define LRO_MAX_AGGR 64
#define PE_MIN_MTU 64 #define PE_MIN_MTU 64
#define PE_MAX_MTU 9000 #define PE_MAX_MTU 9000
#define PE_DEF_MTU ETH_DATA_LEN #define PE_DEF_MTU ETH_DATA_LEN
...@@ -257,37 +252,6 @@ static int pasemi_mac_set_mac_addr(struct net_device *dev, void *p) ...@@ -257,37 +252,6 @@ static int pasemi_mac_set_mac_addr(struct net_device *dev, void *p)
return 0; return 0;
} }
static int get_skb_hdr(struct sk_buff *skb, void **iphdr,
void **tcph, u64 *hdr_flags, void *data)
{
u64 macrx = (u64) data;
unsigned int ip_len;
struct iphdr *iph;
/* IPv4 header checksum failed */
if ((macrx & XCT_MACRX_HTY_M) != XCT_MACRX_HTY_IPV4_OK)
return -1;
/* non tcp packet */
skb_reset_network_header(skb);
iph = ip_hdr(skb);
if (iph->protocol != IPPROTO_TCP)
return -1;
ip_len = ip_hdrlen(skb);
skb_set_transport_header(skb, ip_len);
*tcph = tcp_hdr(skb);
/* check if ip header and tcp header are complete */
if (ntohs(iph->tot_len) < ip_len + tcp_hdrlen(skb))
return -1;
*hdr_flags = LRO_IPV4 | LRO_TCP;
*iphdr = iph;
return 0;
}
static int pasemi_mac_unmap_tx_skb(struct pasemi_mac *mac, static int pasemi_mac_unmap_tx_skb(struct pasemi_mac *mac,
const int nfrags, const int nfrags,
struct sk_buff *skb, struct sk_buff *skb,
...@@ -817,7 +781,7 @@ static int pasemi_mac_clean_rx(struct pasemi_mac_rxring *rx, ...@@ -817,7 +781,7 @@ static int pasemi_mac_clean_rx(struct pasemi_mac_rxring *rx,
skb_put(skb, len-4); skb_put(skb, len-4);
skb->protocol = eth_type_trans(skb, mac->netdev); skb->protocol = eth_type_trans(skb, mac->netdev);
lro_receive_skb(&mac->lro_mgr, skb, (void *)macrx); napi_gro_receive(&mac->napi, skb);
next: next:
RX_DESC(rx, n) = 0; RX_DESC(rx, n) = 0;
...@@ -839,8 +803,6 @@ static int pasemi_mac_clean_rx(struct pasemi_mac_rxring *rx, ...@@ -839,8 +803,6 @@ static int pasemi_mac_clean_rx(struct pasemi_mac_rxring *rx,
rx_ring(mac)->next_to_clean = n; rx_ring(mac)->next_to_clean = n;
lro_flush_all(&mac->lro_mgr);
/* Increase is in number of 16-byte entries, and since each descriptor /* Increase is in number of 16-byte entries, and since each descriptor
* with an 8BRES takes up 3x8 bytes (padded to 4x8), increase with * with an 8BRES takes up 3x8 bytes (padded to 4x8), increase with
* count*2. * count*2.
...@@ -1754,16 +1716,6 @@ pasemi_mac_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -1754,16 +1716,6 @@ pasemi_mac_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
dev->features = NETIF_F_IP_CSUM | NETIF_F_LLTX | NETIF_F_SG | dev->features = NETIF_F_IP_CSUM | NETIF_F_LLTX | NETIF_F_SG |
NETIF_F_HIGHDMA | NETIF_F_GSO; NETIF_F_HIGHDMA | NETIF_F_GSO;
mac->lro_mgr.max_aggr = LRO_MAX_AGGR;
mac->lro_mgr.max_desc = MAX_LRO_DESCRIPTORS;
mac->lro_mgr.lro_arr = mac->lro_desc;
mac->lro_mgr.get_skb_header = get_skb_hdr;
mac->lro_mgr.features = LRO_F_NAPI | LRO_F_EXTRACT_VLAN_ID;
mac->lro_mgr.dev = mac->netdev;
mac->lro_mgr.ip_summed = CHECKSUM_UNNECESSARY;
mac->lro_mgr.ip_summed_aggr = CHECKSUM_UNNECESSARY;
mac->dma_pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa007, NULL); mac->dma_pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa007, NULL);
if (!mac->dma_pdev) { if (!mac->dma_pdev) {
dev_err(&mac->pdev->dev, "Can't find DMA Controller\n"); dev_err(&mac->pdev->dev, "Can't find DMA Controller\n");
......
...@@ -31,7 +31,6 @@ ...@@ -31,7 +31,6 @@
#define CS_RING_SIZE (TX_RING_SIZE*2) #define CS_RING_SIZE (TX_RING_SIZE*2)
#define MAX_LRO_DESCRIPTORS 8
#define MAX_CS 2 #define MAX_CS 2
struct pasemi_mac_txring { struct pasemi_mac_txring {
...@@ -84,10 +83,7 @@ struct pasemi_mac { ...@@ -84,10 +83,7 @@ struct pasemi_mac {
u8 mac_addr[ETH_ALEN]; u8 mac_addr[ETH_ALEN];
struct net_lro_mgr lro_mgr;
struct net_lro_desc lro_desc[MAX_LRO_DESCRIPTORS];
struct timer_list rxtimer; struct timer_list rxtimer;
unsigned int lro_max_aggr;
struct pasemi_mac_txring *tx; struct pasemi_mac_txring *tx;
struct pasemi_mac_rxring *rx; struct pasemi_mac_rxring *rx;
......
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/inet_lro.h>
#include <asm/pasemi_dma.h> #include <asm/pasemi_dma.h>
#include "pasemi_mac.h" #include "pasemi_mac.h"
......
/*
* linux/include/linux/inet_lro.h
*
* Large Receive Offload (ipv4 / tcp)
*
* (C) Copyright IBM Corp. 2007
*
* Authors:
* Jan-Bernd Themann <themann@de.ibm.com>
* Christoph Raisch <raisch@de.ibm.com>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __INET_LRO_H_
#define __INET_LRO_H_
#include <net/ip.h>
#include <net/tcp.h>
/*
* LRO statistics
*/
struct net_lro_stats {
unsigned long aggregated;
unsigned long flushed;
unsigned long no_desc;
};
/*
* LRO descriptor for a tcp session
*/
struct net_lro_desc {
struct sk_buff *parent;
struct sk_buff *last_skb;
struct skb_frag_struct *next_frag;
struct iphdr *iph;
struct tcphdr *tcph;
__wsum data_csum;
__be32 tcp_rcv_tsecr;
__be32 tcp_rcv_tsval;
__be32 tcp_ack;
u32 tcp_next_seq;
u32 skb_tot_frags_len;
u16 ip_tot_len;
u16 tcp_saw_tstamp; /* timestamps enabled */
__be16 tcp_window;
int pkt_aggr_cnt; /* counts aggregated packets */
int vlan_packet;
int mss;
int active;
};
/*
* Large Receive Offload (LRO) Manager
*
* Fields must be set by driver
*/
struct net_lro_mgr {
struct net_device *dev;
struct net_lro_stats stats;
/* LRO features */
unsigned long features;
#define LRO_F_NAPI 1 /* Pass packets to stack via NAPI */
#define LRO_F_EXTRACT_VLAN_ID 2 /* Set flag if VLAN IDs are extracted
from received packets and eth protocol
is still ETH_P_8021Q */
/*
* Set for generated SKBs that are not added to
* the frag list in fragmented mode
*/
u32 ip_summed;
u32 ip_summed_aggr; /* Set in aggregated SKBs: CHECKSUM_UNNECESSARY
* or CHECKSUM_NONE */
int max_desc; /* Max number of LRO descriptors */
int max_aggr; /* Max number of LRO packets to be aggregated */
int frag_align_pad; /* Padding required to properly align layer 3
* headers in generated skb when using frags */
struct net_lro_desc *lro_arr; /* Array of LRO descriptors */
/*
* Optimized driver functions
*
* get_skb_header: returns tcp and ip header for packet in SKB
*/
int (*get_skb_header)(struct sk_buff *skb, void **ip_hdr,
void **tcpudp_hdr, u64 *hdr_flags, void *priv);
/* hdr_flags: */
#define LRO_IPV4 1 /* ip_hdr is IPv4 header */
#define LRO_TCP 2 /* tcpudp_hdr is TCP header */
/*
* get_frag_header: returns mac, tcp and ip header for packet in SKB
*
* @hdr_flags: Indicate what kind of LRO has to be done
* (IPv4/IPv6/TCP/UDP)
*/
int (*get_frag_header)(struct skb_frag_struct *frag, void **mac_hdr,
void **ip_hdr, void **tcpudp_hdr, u64 *hdr_flags,
void *priv);
};
/*
* Processes a SKB
*
* @lro_mgr: LRO manager to use
* @skb: SKB to aggregate
* @priv: Private data that may be used by driver functions
* (for example get_tcp_ip_hdr)
*/
void lro_receive_skb(struct net_lro_mgr *lro_mgr,
struct sk_buff *skb,
void *priv);
/*
* Forward all aggregated SKBs held by lro_mgr to network stack
*/
void lro_flush_all(struct net_lro_mgr *lro_mgr);
#endif
...@@ -406,14 +406,6 @@ config INET_XFRM_MODE_BEET ...@@ -406,14 +406,6 @@ config INET_XFRM_MODE_BEET
If unsure, say Y. If unsure, say Y.
config INET_LRO
tristate "Large Receive Offload (ipv4/tcp)"
default y
---help---
Support for Large Receive Offload (ipv4/tcp).
If unsure, say Y.
config INET_DIAG config INET_DIAG
tristate "INET: socket monitoring interface" tristate "INET: socket monitoring interface"
default y default y
......
...@@ -32,7 +32,6 @@ obj-$(CONFIG_INET_ESP) += esp4.o ...@@ -32,7 +32,6 @@ obj-$(CONFIG_INET_ESP) += esp4.o
obj-$(CONFIG_INET_IPCOMP) += ipcomp.o obj-$(CONFIG_INET_IPCOMP) += ipcomp.o
obj-$(CONFIG_INET_XFRM_TUNNEL) += xfrm4_tunnel.o obj-$(CONFIG_INET_XFRM_TUNNEL) += xfrm4_tunnel.o
obj-$(CONFIG_INET_XFRM_MODE_BEET) += xfrm4_mode_beet.o obj-$(CONFIG_INET_XFRM_MODE_BEET) += xfrm4_mode_beet.o
obj-$(CONFIG_INET_LRO) += inet_lro.o
obj-$(CONFIG_INET_TUNNEL) += tunnel4.o obj-$(CONFIG_INET_TUNNEL) += tunnel4.o
obj-$(CONFIG_INET_XFRM_MODE_TRANSPORT) += xfrm4_mode_transport.o obj-$(CONFIG_INET_XFRM_MODE_TRANSPORT) += xfrm4_mode_transport.o
obj-$(CONFIG_INET_XFRM_MODE_TUNNEL) += xfrm4_mode_tunnel.o obj-$(CONFIG_INET_XFRM_MODE_TUNNEL) += xfrm4_mode_tunnel.o
......
/*
* linux/net/ipv4/inet_lro.c
*
* Large Receive Offload (ipv4 / tcp)
*
* (C) Copyright IBM Corp. 2007
*
* Authors:
* Jan-Bernd Themann <themann@de.ibm.com>
* Christoph Raisch <raisch@de.ibm.com>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/if_vlan.h>
#include <linux/inet_lro.h>
#include <net/checksum.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jan-Bernd Themann <themann@de.ibm.com>");
MODULE_DESCRIPTION("Large Receive Offload (ipv4 / tcp)");
#define TCP_HDR_LEN(tcph) (tcph->doff << 2)
#define IP_HDR_LEN(iph) (iph->ihl << 2)
#define TCP_PAYLOAD_LENGTH(iph, tcph) \
(ntohs(iph->tot_len) - IP_HDR_LEN(iph) - TCP_HDR_LEN(tcph))
#define IPH_LEN_WO_OPTIONS 5
#define TCPH_LEN_WO_OPTIONS 5
#define TCPH_LEN_W_TIMESTAMP 8
#define LRO_MAX_PG_HLEN 64
#define LRO_INC_STATS(lro_mgr, attr) { lro_mgr->stats.attr++; }
/*
* Basic tcp checks whether packet is suitable for LRO
*/
static int lro_tcp_ip_check(const struct iphdr *iph, const struct tcphdr *tcph,
int len, const struct net_lro_desc *lro_desc)
{
/* check ip header: don't aggregate padded frames */
if (ntohs(iph->tot_len) != len)
return -1;
if (TCP_PAYLOAD_LENGTH(iph, tcph) == 0)
return -1;
if (iph->ihl != IPH_LEN_WO_OPTIONS)
return -1;
if (tcph->cwr || tcph->ece || tcph->urg || !tcph->ack ||
tcph->rst || tcph->syn || tcph->fin)
return -1;
if (INET_ECN_is_ce(ipv4_get_dsfield(iph)))
return -1;
if (tcph->doff != TCPH_LEN_WO_OPTIONS &&
tcph->doff != TCPH_LEN_W_TIMESTAMP)
return -1;
/* check tcp options (only timestamp allowed) */
if (tcph->doff == TCPH_LEN_W_TIMESTAMP) {
__be32 *topt = (__be32 *)(tcph + 1);
if (*topt != htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16)
| (TCPOPT_TIMESTAMP << 8)
| TCPOLEN_TIMESTAMP))
return -1;
/* timestamp should be in right order */
topt++;
if (lro_desc && after(ntohl(lro_desc->tcp_rcv_tsval),
ntohl(*topt)))
return -1;
/* timestamp reply should not be zero */
topt++;
if (*topt == 0)
return -1;
}
return 0;
}
static void lro_update_tcp_ip_header(struct net_lro_desc *lro_desc)
{
struct iphdr *iph = lro_desc->iph;
struct tcphdr *tcph = lro_desc->tcph;
__be32 *p;
__wsum tcp_hdr_csum;
tcph->ack_seq = lro_desc->tcp_ack;
tcph->window = lro_desc->tcp_window;
if (lro_desc->tcp_saw_tstamp) {
p = (__be32 *)(tcph + 1);
*(p+2) = lro_desc->tcp_rcv_tsecr;
}
csum_replace2(&iph->check, iph->tot_len, htons(lro_desc->ip_tot_len));
iph->tot_len = htons(lro_desc->ip_tot_len);
tcph->check = 0;
tcp_hdr_csum = csum_partial(tcph, TCP_HDR_LEN(tcph), 0);
lro_desc->data_csum = csum_add(lro_desc->data_csum, tcp_hdr_csum);
tcph->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
lro_desc->ip_tot_len -
IP_HDR_LEN(iph), IPPROTO_TCP,
lro_desc->data_csum);
}
static __wsum lro_tcp_data_csum(struct iphdr *iph, struct tcphdr *tcph, int len)
{
__wsum tcp_csum;
__wsum tcp_hdr_csum;
__wsum tcp_ps_hdr_csum;
tcp_csum = ~csum_unfold(tcph->check);
tcp_hdr_csum = csum_partial(tcph, TCP_HDR_LEN(tcph), tcp_csum);
tcp_ps_hdr_csum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
len + TCP_HDR_LEN(tcph),
IPPROTO_TCP, 0);
return csum_sub(csum_sub(tcp_csum, tcp_hdr_csum),
tcp_ps_hdr_csum);
}
static void lro_init_desc(struct net_lro_desc *lro_desc, struct sk_buff *skb,
struct iphdr *iph, struct tcphdr *tcph)
{
int nr_frags;
__be32 *ptr;
u32 tcp_data_len = TCP_PAYLOAD_LENGTH(iph, tcph);
nr_frags = skb_shinfo(skb)->nr_frags;
lro_desc->parent = skb;
lro_desc->next_frag = &(skb_shinfo(skb)->frags[nr_frags]);
lro_desc->iph = iph;
lro_desc->tcph = tcph;
lro_desc->tcp_next_seq = ntohl(tcph->seq) + tcp_data_len;
lro_desc->tcp_ack = tcph->ack_seq;
lro_desc->tcp_window = tcph->window;
lro_desc->pkt_aggr_cnt = 1;
lro_desc->ip_tot_len = ntohs(iph->tot_len);
if (tcph->doff == 8) {
ptr = (__be32 *)(tcph+1);
lro_desc->tcp_saw_tstamp = 1;
lro_desc->tcp_rcv_tsval = *(ptr+1);
lro_desc->tcp_rcv_tsecr = *(ptr+2);
}
lro_desc->mss = tcp_data_len;
lro_desc->active = 1;
lro_desc->data_csum = lro_tcp_data_csum(iph, tcph,
tcp_data_len);
}
static inline void lro_clear_desc(struct net_lro_desc *lro_desc)
{
memset(lro_desc, 0, sizeof(struct net_lro_desc));
}
static void lro_add_common(struct net_lro_desc *lro_desc, struct iphdr *iph,
struct tcphdr *tcph, int tcp_data_len)
{
struct sk_buff *parent = lro_desc->parent;
__be32 *topt;
lro_desc->pkt_aggr_cnt++;
lro_desc->ip_tot_len += tcp_data_len;
lro_desc->tcp_next_seq += tcp_data_len;
lro_desc->tcp_window = tcph->window;
lro_desc->tcp_ack = tcph->ack_seq;
/* don't update tcp_rcv_tsval, would not work with PAWS */
if (lro_desc->tcp_saw_tstamp) {
topt = (__be32 *) (tcph + 1);
lro_desc->tcp_rcv_tsecr = *(topt + 2);
}
lro_desc->data_csum = csum_block_add(lro_desc->data_csum,
lro_tcp_data_csum(iph, tcph,
tcp_data_len),
parent->len);
parent->len += tcp_data_len;
parent->data_len += tcp_data_len;
if (tcp_data_len > lro_desc->mss)
lro_desc->mss = tcp_data_len;
}
static void lro_add_packet(struct net_lro_desc *lro_desc, struct sk_buff *skb,
struct iphdr *iph, struct tcphdr *tcph)
{
struct sk_buff *parent = lro_desc->parent;
int tcp_data_len = TCP_PAYLOAD_LENGTH(iph, tcph);
lro_add_common(lro_desc, iph, tcph, tcp_data_len);
skb_pull(skb, (skb->len - tcp_data_len));
parent->truesize += skb->truesize;
if (lro_desc->last_skb)
lro_desc->last_skb->next = skb;
else
skb_shinfo(parent)->frag_list = skb;
lro_desc->last_skb = skb;
}
static int lro_check_tcp_conn(struct net_lro_desc *lro_desc,
struct iphdr *iph,
struct tcphdr *tcph)
{
if ((lro_desc->iph->saddr != iph->saddr) ||
(lro_desc->iph->daddr != iph->daddr) ||
(lro_desc->tcph->source != tcph->source) ||
(lro_desc->tcph->dest != tcph->dest))
return -1;
return 0;
}
static struct net_lro_desc *lro_get_desc(struct net_lro_mgr *lro_mgr,
struct net_lro_desc *lro_arr,
struct iphdr *iph,
struct tcphdr *tcph)
{
struct net_lro_desc *lro_desc = NULL;
struct net_lro_desc *tmp;
int max_desc = lro_mgr->max_desc;
int i;
for (i = 0; i < max_desc; i++) {
tmp = &lro_arr[i];
if (tmp->active)
if (!lro_check_tcp_conn(tmp, iph, tcph)) {
lro_desc = tmp;
goto out;
}
}
for (i = 0; i < max_desc; i++) {
if (!lro_arr[i].active) {
lro_desc = &lro_arr[i];
goto out;
}
}
LRO_INC_STATS(lro_mgr, no_desc);
out:
return lro_desc;
}
static void lro_flush(struct net_lro_mgr *lro_mgr,
struct net_lro_desc *lro_desc)
{
if (lro_desc->pkt_aggr_cnt > 1)
lro_update_tcp_ip_header(lro_desc);
skb_shinfo(lro_desc->parent)->gso_size = lro_desc->mss;
if (lro_mgr->features & LRO_F_NAPI)
netif_receive_skb(lro_desc->parent);
else
netif_rx(lro_desc->parent);
LRO_INC_STATS(lro_mgr, flushed);
lro_clear_desc(lro_desc);
}
static int __lro_proc_skb(struct net_lro_mgr *lro_mgr, struct sk_buff *skb,
void *priv)
{
struct net_lro_desc *lro_desc;
struct iphdr *iph;
struct tcphdr *tcph;
u64 flags;
int vlan_hdr_len = 0;
if (!lro_mgr->get_skb_header ||
lro_mgr->get_skb_header(skb, (void *)&iph, (void *)&tcph,
&flags, priv))
goto out;
if (!(flags & LRO_IPV4) || !(flags & LRO_TCP))
goto out;
lro_desc = lro_get_desc(lro_mgr, lro_mgr->lro_arr, iph, tcph);
if (!lro_desc)
goto out;
if ((skb->protocol == htons(ETH_P_8021Q)) &&
!(lro_mgr->features & LRO_F_EXTRACT_VLAN_ID))
vlan_hdr_len = VLAN_HLEN;
if (!lro_desc->active) { /* start new lro session */
if (lro_tcp_ip_check(iph, tcph, skb->len - vlan_hdr_len, NULL))
goto out;
skb->ip_summed = lro_mgr->ip_summed_aggr;
lro_init_desc(lro_desc, skb, iph, tcph);
LRO_INC_STATS(lro_mgr, aggregated);
return 0;
}
if (lro_desc->tcp_next_seq != ntohl(tcph->seq))
goto out2;
if (lro_tcp_ip_check(iph, tcph, skb->len, lro_desc))
goto out2;
lro_add_packet(lro_desc, skb, iph, tcph);
LRO_INC_STATS(lro_mgr, aggregated);
if ((lro_desc->pkt_aggr_cnt >= lro_mgr->max_aggr) ||
lro_desc->parent->len > (0xFFFF - lro_mgr->dev->mtu))
lro_flush(lro_mgr, lro_desc);
return 0;
out2: /* send aggregated SKBs to stack */
lro_flush(lro_mgr, lro_desc);
out:
return 1;
}
void lro_receive_skb(struct net_lro_mgr *lro_mgr,
struct sk_buff *skb,
void *priv)
{
if (__lro_proc_skb(lro_mgr, skb, priv)) {
if (lro_mgr->features & LRO_F_NAPI)
netif_receive_skb(skb);
else
netif_rx(skb);
}
}
EXPORT_SYMBOL(lro_receive_skb);
void lro_flush_all(struct net_lro_mgr *lro_mgr)
{
int i;
struct net_lro_desc *lro_desc = lro_mgr->lro_arr;
for (i = 0; i < lro_mgr->max_desc; i++) {
if (lro_desc[i].active)
lro_flush(lro_mgr, &lro_desc[i]);
}
}
EXPORT_SYMBOL(lro_flush_all);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment