Merge branch 'lro'
This commit is contained in:
commit
2ade43618b
|
@ -57,16 +57,20 @@
|
|||
#include <linux/ethtool.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <net/tcp.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/div64.h>
|
||||
|
||||
/* local include */
|
||||
#include "s2io.h"
|
||||
#include "s2io-regs.h"
|
||||
|
||||
#define DRV_VERSION "Version 2.0.9.4"
|
||||
#define DRV_VERSION "2.0.11.2"
|
||||
|
||||
/* S2io Driver name & version. */
|
||||
static char s2io_driver_name[] = "Neterion";
|
||||
|
@ -168,6 +172,11 @@ static char ethtool_stats_keys[][ETH_GSTRING_LEN] = {
|
|||
{"\n DRIVER STATISTICS"},
|
||||
{"single_bit_ecc_errs"},
|
||||
{"double_bit_ecc_errs"},
|
||||
("lro_aggregated_pkts"),
|
||||
("lro_flush_both_count"),
|
||||
("lro_out_of_sequence_pkts"),
|
||||
("lro_flush_due_to_max_pkts"),
|
||||
("lro_avg_aggr_pkts"),
|
||||
};
|
||||
|
||||
#define S2IO_STAT_LEN sizeof(ethtool_stats_keys)/ ETH_GSTRING_LEN
|
||||
|
@ -317,6 +326,12 @@ static unsigned int indicate_max_pkts;
|
|||
static unsigned int rxsync_frequency = 3;
|
||||
/* Interrupt type. Values can be 0(INTA), 1(MSI), 2(MSI_X) */
|
||||
static unsigned int intr_type = 0;
|
||||
/* Large receive offload feature */
|
||||
static unsigned int lro = 0;
|
||||
/* Max pkts to be aggregated by LRO at one time. If not specified,
|
||||
* aggregation happens until we hit max IP pkt size(64K)
|
||||
*/
|
||||
static unsigned int lro_max_pkts = 0xFFFF;
|
||||
|
||||
/*
|
||||
* S2IO device table.
|
||||
|
@ -1476,6 +1491,19 @@ static int init_nic(struct s2io_nic *nic)
|
|||
writel((u32) (val64 >> 32), (add + 4));
|
||||
val64 = readq(&bar0->mac_cfg);
|
||||
|
||||
/* Enable FCS stripping by adapter */
|
||||
add = &bar0->mac_cfg;
|
||||
val64 = readq(&bar0->mac_cfg);
|
||||
val64 |= MAC_CFG_RMAC_STRIP_FCS;
|
||||
if (nic->device_type == XFRAME_II_DEVICE)
|
||||
writeq(val64, &bar0->mac_cfg);
|
||||
else {
|
||||
writeq(RMAC_CFG_KEY(0x4C0D), &bar0->rmac_cfg_key);
|
||||
writel((u32) (val64), add);
|
||||
writeq(RMAC_CFG_KEY(0x4C0D), &bar0->rmac_cfg_key);
|
||||
writel((u32) (val64 >> 32), (add + 4));
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the time value to be inserted in the pause frame
|
||||
* generated by xena.
|
||||
|
@ -2569,6 +2597,8 @@ static void rx_intr_handler(ring_info_t *ring_data)
|
|||
#ifndef CONFIG_S2IO_NAPI
|
||||
int pkt_cnt = 0;
|
||||
#endif
|
||||
int i;
|
||||
|
||||
spin_lock(&nic->rx_lock);
|
||||
if (atomic_read(&nic->card_state) == CARD_DOWN) {
|
||||
DBG_PRINT(INTR_DBG, "%s: %s going down for reset\n",
|
||||
|
@ -2661,6 +2691,18 @@ static void rx_intr_handler(ring_info_t *ring_data)
|
|||
break;
|
||||
#endif
|
||||
}
|
||||
if (nic->lro) {
|
||||
/* Clear all LRO sessions before exiting */
|
||||
for (i=0; i<MAX_LRO_SESSIONS; i++) {
|
||||
lro_t *lro = &nic->lro0_n[i];
|
||||
if (lro->in_use) {
|
||||
update_L3L4_header(nic, lro);
|
||||
queue_rx_frame(lro->parent);
|
||||
clear_lro_session(lro);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock(&nic->rx_lock);
|
||||
}
|
||||
|
||||
|
@ -3668,23 +3710,32 @@ s2io_msi_handle(int irq, void *dev_id, struct pt_regs *regs)
|
|||
* else schedule a tasklet to reallocate the buffers.
|
||||
*/
|
||||
for (i = 0; i < config->rx_ring_num; i++) {
|
||||
int rxb_size = atomic_read(&sp->rx_bufs_left[i]);
|
||||
int level = rx_buffer_level(sp, rxb_size, i);
|
||||
if (!sp->lro) {
|
||||
int rxb_size = atomic_read(&sp->rx_bufs_left[i]);
|
||||
int level = rx_buffer_level(sp, rxb_size, i);
|
||||
|
||||
if ((level == PANIC) && (!TASKLET_IN_USE)) {
|
||||
DBG_PRINT(INTR_DBG, "%s: Rx BD hit ", dev->name);
|
||||
DBG_PRINT(INTR_DBG, "PANIC levels\n");
|
||||
if ((ret = fill_rx_buffers(sp, i)) == -ENOMEM) {
|
||||
DBG_PRINT(ERR_DBG, "%s:Out of memory",
|
||||
dev->name);
|
||||
DBG_PRINT(ERR_DBG, " in ISR!!\n");
|
||||
if ((level == PANIC) && (!TASKLET_IN_USE)) {
|
||||
DBG_PRINT(INTR_DBG, "%s: Rx BD hit ",
|
||||
dev->name);
|
||||
DBG_PRINT(INTR_DBG, "PANIC levels\n");
|
||||
if ((ret = fill_rx_buffers(sp, i)) == -ENOMEM) {
|
||||
DBG_PRINT(ERR_DBG, "%s:Out of memory",
|
||||
dev->name);
|
||||
DBG_PRINT(ERR_DBG, " in ISR!!\n");
|
||||
clear_bit(0, (&sp->tasklet_status));
|
||||
atomic_dec(&sp->isr_cnt);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
clear_bit(0, (&sp->tasklet_status));
|
||||
atomic_dec(&sp->isr_cnt);
|
||||
return IRQ_HANDLED;
|
||||
} else if (level == LOW) {
|
||||
tasklet_schedule(&sp->task);
|
||||
}
|
||||
clear_bit(0, (&sp->tasklet_status));
|
||||
} else if (level == LOW) {
|
||||
tasklet_schedule(&sp->task);
|
||||
}
|
||||
else if (fill_rx_buffers(sp, i) == -ENOMEM) {
|
||||
DBG_PRINT(ERR_DBG, "%s:Out of memory",
|
||||
dev->name);
|
||||
DBG_PRINT(ERR_DBG, " in Rx Intr!!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3697,29 +3748,37 @@ s2io_msix_ring_handle(int irq, void *dev_id, struct pt_regs *regs)
|
|||
{
|
||||
ring_info_t *ring = (ring_info_t *)dev_id;
|
||||
nic_t *sp = ring->nic;
|
||||
struct net_device *dev = (struct net_device *) dev_id;
|
||||
int rxb_size, level, rng_n;
|
||||
|
||||
atomic_inc(&sp->isr_cnt);
|
||||
rx_intr_handler(ring);
|
||||
|
||||
rng_n = ring->ring_no;
|
||||
rxb_size = atomic_read(&sp->rx_bufs_left[rng_n]);
|
||||
level = rx_buffer_level(sp, rxb_size, rng_n);
|
||||
if (!sp->lro) {
|
||||
rxb_size = atomic_read(&sp->rx_bufs_left[rng_n]);
|
||||
level = rx_buffer_level(sp, rxb_size, rng_n);
|
||||
|
||||
if ((level == PANIC) && (!TASKLET_IN_USE)) {
|
||||
int ret;
|
||||
DBG_PRINT(INTR_DBG, "%s: Rx BD hit ", __FUNCTION__);
|
||||
DBG_PRINT(INTR_DBG, "PANIC levels\n");
|
||||
if ((ret = fill_rx_buffers(sp, rng_n)) == -ENOMEM) {
|
||||
DBG_PRINT(ERR_DBG, "Out of memory in %s",
|
||||
__FUNCTION__);
|
||||
if ((level == PANIC) && (!TASKLET_IN_USE)) {
|
||||
int ret;
|
||||
DBG_PRINT(INTR_DBG, "%s: Rx BD hit ", __FUNCTION__);
|
||||
DBG_PRINT(INTR_DBG, "PANIC levels\n");
|
||||
if ((ret = fill_rx_buffers(sp, rng_n)) == -ENOMEM) {
|
||||
DBG_PRINT(ERR_DBG, "Out of memory in %s",
|
||||
__FUNCTION__);
|
||||
clear_bit(0, (&sp->tasklet_status));
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
clear_bit(0, (&sp->tasklet_status));
|
||||
return IRQ_HANDLED;
|
||||
} else if (level == LOW) {
|
||||
tasklet_schedule(&sp->task);
|
||||
}
|
||||
clear_bit(0, (&sp->tasklet_status));
|
||||
} else if (level == LOW) {
|
||||
tasklet_schedule(&sp->task);
|
||||
}
|
||||
else if (fill_rx_buffers(sp, rng_n) == -ENOMEM) {
|
||||
DBG_PRINT(ERR_DBG, "%s:Out of memory", dev->name);
|
||||
DBG_PRINT(ERR_DBG, " in Rx Intr!!\n");
|
||||
}
|
||||
|
||||
atomic_dec(&sp->isr_cnt);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
@ -3875,24 +3934,33 @@ static irqreturn_t s2io_isr(int irq, void *dev_id, struct pt_regs *regs)
|
|||
*/
|
||||
#ifndef CONFIG_S2IO_NAPI
|
||||
for (i = 0; i < config->rx_ring_num; i++) {
|
||||
int ret;
|
||||
int rxb_size = atomic_read(&sp->rx_bufs_left[i]);
|
||||
int level = rx_buffer_level(sp, rxb_size, i);
|
||||
if (!sp->lro) {
|
||||
int ret;
|
||||
int rxb_size = atomic_read(&sp->rx_bufs_left[i]);
|
||||
int level = rx_buffer_level(sp, rxb_size, i);
|
||||
|
||||
if ((level == PANIC) && (!TASKLET_IN_USE)) {
|
||||
DBG_PRINT(INTR_DBG, "%s: Rx BD hit ", dev->name);
|
||||
DBG_PRINT(INTR_DBG, "PANIC levels\n");
|
||||
if ((ret = fill_rx_buffers(sp, i)) == -ENOMEM) {
|
||||
DBG_PRINT(ERR_DBG, "%s:Out of memory",
|
||||
dev->name);
|
||||
DBG_PRINT(ERR_DBG, " in ISR!!\n");
|
||||
if ((level == PANIC) && (!TASKLET_IN_USE)) {
|
||||
DBG_PRINT(INTR_DBG, "%s: Rx BD hit ",
|
||||
dev->name);
|
||||
DBG_PRINT(INTR_DBG, "PANIC levels\n");
|
||||
if ((ret = fill_rx_buffers(sp, i)) == -ENOMEM) {
|
||||
DBG_PRINT(ERR_DBG, "%s:Out of memory",
|
||||
dev->name);
|
||||
DBG_PRINT(ERR_DBG, " in ISR!!\n");
|
||||
clear_bit(0, (&sp->tasklet_status));
|
||||
atomic_dec(&sp->isr_cnt);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
clear_bit(0, (&sp->tasklet_status));
|
||||
atomic_dec(&sp->isr_cnt);
|
||||
return IRQ_HANDLED;
|
||||
} else if (level == LOW) {
|
||||
tasklet_schedule(&sp->task);
|
||||
}
|
||||
clear_bit(0, (&sp->tasklet_status));
|
||||
} else if (level == LOW) {
|
||||
tasklet_schedule(&sp->task);
|
||||
}
|
||||
else if (fill_rx_buffers(sp, i) == -ENOMEM) {
|
||||
DBG_PRINT(ERR_DBG, "%s:Out of memory",
|
||||
dev->name);
|
||||
DBG_PRINT(ERR_DBG, " in Rx intr!!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -5043,6 +5111,7 @@ static void s2io_get_ethtool_stats(struct net_device *dev,
|
|||
int i = 0;
|
||||
nic_t *sp = dev->priv;
|
||||
StatInfo_t *stat_info = sp->mac_control.stats_info;
|
||||
u64 tmp;
|
||||
|
||||
s2io_updt_stats(sp);
|
||||
tmp_stats[i++] =
|
||||
|
@ -5134,6 +5203,16 @@ static void s2io_get_ethtool_stats(struct net_device *dev,
|
|||
tmp_stats[i++] = 0;
|
||||
tmp_stats[i++] = stat_info->sw_stat.single_ecc_errs;
|
||||
tmp_stats[i++] = stat_info->sw_stat.double_ecc_errs;
|
||||
tmp_stats[i++] = stat_info->sw_stat.clubbed_frms_cnt;
|
||||
tmp_stats[i++] = stat_info->sw_stat.sending_both;
|
||||
tmp_stats[i++] = stat_info->sw_stat.outof_sequence_pkts;
|
||||
tmp_stats[i++] = stat_info->sw_stat.flush_max_pkts;
|
||||
tmp = 0;
|
||||
if (stat_info->sw_stat.num_aggregations) {
|
||||
tmp = stat_info->sw_stat.sum_avg_pkts_aggregated;
|
||||
do_div(tmp, stat_info->sw_stat.num_aggregations);
|
||||
}
|
||||
tmp_stats[i++] = tmp;
|
||||
}
|
||||
|
||||
static int s2io_ethtool_get_regs_len(struct net_device *dev)
|
||||
|
@ -5515,6 +5594,14 @@ static int s2io_card_up(nic_t * sp)
|
|||
/* Setting its receive mode */
|
||||
s2io_set_multicast(dev);
|
||||
|
||||
if (sp->lro) {
|
||||
/* Initialize max aggregatable pkts based on MTU */
|
||||
sp->lro_max_aggr_per_sess = ((1<<16) - 1) / dev->mtu;
|
||||
/* Check if we can use(if specified) user provided value */
|
||||
if (lro_max_pkts < sp->lro_max_aggr_per_sess)
|
||||
sp->lro_max_aggr_per_sess = lro_max_pkts;
|
||||
}
|
||||
|
||||
/* Enable tasklet for the device */
|
||||
tasklet_init(&sp->task, s2io_tasklet, (unsigned long) dev);
|
||||
|
||||
|
@ -5607,6 +5694,7 @@ static int rx_osm_handler(ring_info_t *ring_data, RxD_t * rxdp)
|
|||
((unsigned long) rxdp->Host_Control);
|
||||
int ring_no = ring_data->ring_no;
|
||||
u16 l3_csum, l4_csum;
|
||||
lro_t *lro;
|
||||
|
||||
skb->dev = dev;
|
||||
if (rxdp->Control_1 & RXD_T_CODE) {
|
||||
|
@ -5655,7 +5743,8 @@ static int rx_osm_handler(ring_info_t *ring_data, RxD_t * rxdp)
|
|||
skb_put(skb, buf2_len);
|
||||
}
|
||||
|
||||
if ((rxdp->Control_1 & TCP_OR_UDP_FRAME) &&
|
||||
if ((rxdp->Control_1 & TCP_OR_UDP_FRAME) && ((!sp->lro) ||
|
||||
(sp->lro && (!(rxdp->Control_1 & RXD_FRAME_IP_FRAG)))) &&
|
||||
(sp->rx_csum)) {
|
||||
l3_csum = RXD_GET_L3_CKSUM(rxdp->Control_1);
|
||||
l4_csum = RXD_GET_L4_CKSUM(rxdp->Control_1);
|
||||
|
@ -5666,6 +5755,54 @@ static int rx_osm_handler(ring_info_t *ring_data, RxD_t * rxdp)
|
|||
* a flag in the RxD.
|
||||
*/
|
||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
if (sp->lro) {
|
||||
u32 tcp_len;
|
||||
u8 *tcp;
|
||||
int ret = 0;
|
||||
|
||||
ret = s2io_club_tcp_session(skb->data, &tcp,
|
||||
&tcp_len, &lro, rxdp, sp);
|
||||
switch (ret) {
|
||||
case 3: /* Begin anew */
|
||||
lro->parent = skb;
|
||||
goto aggregate;
|
||||
case 1: /* Aggregate */
|
||||
{
|
||||
lro_append_pkt(sp, lro,
|
||||
skb, tcp_len);
|
||||
goto aggregate;
|
||||
}
|
||||
case 4: /* Flush session */
|
||||
{
|
||||
lro_append_pkt(sp, lro,
|
||||
skb, tcp_len);
|
||||
queue_rx_frame(lro->parent);
|
||||
clear_lro_session(lro);
|
||||
sp->mac_control.stats_info->
|
||||
sw_stat.flush_max_pkts++;
|
||||
goto aggregate;
|
||||
}
|
||||
case 2: /* Flush both */
|
||||
lro->parent->data_len =
|
||||
lro->frags_len;
|
||||
sp->mac_control.stats_info->
|
||||
sw_stat.sending_both++;
|
||||
queue_rx_frame(lro->parent);
|
||||
clear_lro_session(lro);
|
||||
goto send_up;
|
||||
case 0: /* sessions exceeded */
|
||||
case 5: /*
|
||||
* First pkt in session not
|
||||
* L3/L4 aggregatable
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
DBG_PRINT(ERR_DBG,
|
||||
"%s: Samadhana!!\n",
|
||||
__FUNCTION__);
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Packet with erroneous checksum, let the
|
||||
|
@ -5677,25 +5814,31 @@ static int rx_osm_handler(ring_info_t *ring_data, RxD_t * rxdp)
|
|||
skb->ip_summed = CHECKSUM_NONE;
|
||||
}
|
||||
|
||||
skb->protocol = eth_type_trans(skb, dev);
|
||||
if (!sp->lro) {
|
||||
skb->protocol = eth_type_trans(skb, dev);
|
||||
#ifdef CONFIG_S2IO_NAPI
|
||||
if (sp->vlgrp && RXD_GET_VLAN_TAG(rxdp->Control_2)) {
|
||||
/* Queueing the vlan frame to the upper layer */
|
||||
vlan_hwaccel_receive_skb(skb, sp->vlgrp,
|
||||
RXD_GET_VLAN_TAG(rxdp->Control_2));
|
||||
} else {
|
||||
netif_receive_skb(skb);
|
||||
}
|
||||
if (sp->vlgrp && RXD_GET_VLAN_TAG(rxdp->Control_2)) {
|
||||
/* Queueing the vlan frame to the upper layer */
|
||||
vlan_hwaccel_receive_skb(skb, sp->vlgrp,
|
||||
RXD_GET_VLAN_TAG(rxdp->Control_2));
|
||||
} else {
|
||||
netif_receive_skb(skb);
|
||||
}
|
||||
#else
|
||||
if (sp->vlgrp && RXD_GET_VLAN_TAG(rxdp->Control_2)) {
|
||||
/* Queueing the vlan frame to the upper layer */
|
||||
vlan_hwaccel_rx(skb, sp->vlgrp,
|
||||
RXD_GET_VLAN_TAG(rxdp->Control_2));
|
||||
} else {
|
||||
netif_rx(skb);
|
||||
}
|
||||
if (sp->vlgrp && RXD_GET_VLAN_TAG(rxdp->Control_2)) {
|
||||
/* Queueing the vlan frame to the upper layer */
|
||||
vlan_hwaccel_rx(skb, sp->vlgrp,
|
||||
RXD_GET_VLAN_TAG(rxdp->Control_2));
|
||||
} else {
|
||||
netif_rx(skb);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
send_up:
|
||||
queue_rx_frame(skb);
|
||||
}
|
||||
dev->last_rx = jiffies;
|
||||
aggregate:
|
||||
atomic_dec(&sp->rx_bufs_left[ring_no]);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
@ -5807,6 +5950,8 @@ module_param(indicate_max_pkts, int, 0);
|
|||
#endif
|
||||
module_param(rxsync_frequency, int, 0);
|
||||
module_param(intr_type, int, 0);
|
||||
module_param(lro, int, 0);
|
||||
module_param(lro_max_pkts, int, 0);
|
||||
|
||||
/**
|
||||
* s2io_init_nic - Initialization of the adapter .
|
||||
|
@ -5938,6 +6083,7 @@ Defaulting to INTA\n");
|
|||
else
|
||||
sp->device_type = XFRAME_I_DEVICE;
|
||||
|
||||
sp->lro = lro;
|
||||
|
||||
/* Initialize some PCI/PCI-X fields of the NIC. */
|
||||
s2io_init_pci(sp);
|
||||
|
@ -6241,6 +6387,10 @@ Defaulting to INTA\n");
|
|||
DBG_PRINT(ERR_DBG, "%s: 3-Buffer mode support has been "
|
||||
"enabled\n",dev->name);
|
||||
|
||||
if (sp->lro)
|
||||
DBG_PRINT(ERR_DBG, "%s: Large receive offload enabled\n",
|
||||
dev->name);
|
||||
|
||||
/* Initialize device name */
|
||||
strcpy(sp->name, dev->name);
|
||||
if (sp->device_type & XFRAME_II_DEVICE)
|
||||
|
@ -6351,3 +6501,318 @@ static void s2io_closer(void)
|
|||
|
||||
module_init(s2io_starter);
|
||||
module_exit(s2io_closer);
|
||||
|
||||
static int check_L2_lro_capable(u8 *buffer, struct iphdr **ip,
|
||||
struct tcphdr **tcp, RxD_t *rxdp)
|
||||
{
|
||||
int ip_off;
|
||||
u8 l2_type = (u8)((rxdp->Control_1 >> 37) & 0x7), ip_len;
|
||||
|
||||
if (!(rxdp->Control_1 & RXD_FRAME_PROTO_TCP)) {
|
||||
DBG_PRINT(INIT_DBG,"%s: Non-TCP frames not supported for LRO\n",
|
||||
__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* TODO:
|
||||
* By default the VLAN field in the MAC is stripped by the card, if this
|
||||
* feature is turned off in rx_pa_cfg register, then the ip_off field
|
||||
* has to be shifted by a further 2 bytes
|
||||
*/
|
||||
switch (l2_type) {
|
||||
case 0: /* DIX type */
|
||||
case 4: /* DIX type with VLAN */
|
||||
ip_off = HEADER_ETHERNET_II_802_3_SIZE;
|
||||
break;
|
||||
/* LLC, SNAP etc are considered non-mergeable */
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
*ip = (struct iphdr *)((u8 *)buffer + ip_off);
|
||||
ip_len = (u8)((*ip)->ihl);
|
||||
ip_len <<= 2;
|
||||
*tcp = (struct tcphdr *)((unsigned long)*ip + ip_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_for_socket_match(lro_t *lro, struct iphdr *ip,
|
||||
struct tcphdr *tcp)
|
||||
{
|
||||
DBG_PRINT(INFO_DBG,"%s: Been here...\n", __FUNCTION__);
|
||||
if ((lro->iph->saddr != ip->saddr) || (lro->iph->daddr != ip->daddr) ||
|
||||
(lro->tcph->source != tcp->source) || (lro->tcph->dest != tcp->dest))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int get_l4_pyld_length(struct iphdr *ip, struct tcphdr *tcp)
|
||||
{
|
||||
return(ntohs(ip->tot_len) - (ip->ihl << 2) - (tcp->doff << 2));
|
||||
}
|
||||
|
||||
static void initiate_new_session(lro_t *lro, u8 *l2h,
|
||||
struct iphdr *ip, struct tcphdr *tcp, u32 tcp_pyld_len)
|
||||
{
|
||||
DBG_PRINT(INFO_DBG,"%s: Been here...\n", __FUNCTION__);
|
||||
lro->l2h = l2h;
|
||||
lro->iph = ip;
|
||||
lro->tcph = tcp;
|
||||
lro->tcp_next_seq = tcp_pyld_len + ntohl(tcp->seq);
|
||||
lro->tcp_ack = ntohl(tcp->ack_seq);
|
||||
lro->sg_num = 1;
|
||||
lro->total_len = ntohs(ip->tot_len);
|
||||
lro->frags_len = 0;
|
||||
/*
|
||||
* check if we saw TCP timestamp. Other consistency checks have
|
||||
* already been done.
|
||||
*/
|
||||
if (tcp->doff == 8) {
|
||||
u32 *ptr;
|
||||
ptr = (u32 *)(tcp+1);
|
||||
lro->saw_ts = 1;
|
||||
lro->cur_tsval = *(ptr+1);
|
||||
lro->cur_tsecr = *(ptr+2);
|
||||
}
|
||||
lro->in_use = 1;
|
||||
}
|
||||
|
||||
static void update_L3L4_header(nic_t *sp, lro_t *lro)
|
||||
{
|
||||
struct iphdr *ip = lro->iph;
|
||||
struct tcphdr *tcp = lro->tcph;
|
||||
u16 nchk;
|
||||
StatInfo_t *statinfo = sp->mac_control.stats_info;
|
||||
DBG_PRINT(INFO_DBG,"%s: Been here...\n", __FUNCTION__);
|
||||
|
||||
/* Update L3 header */
|
||||
ip->tot_len = htons(lro->total_len);
|
||||
ip->check = 0;
|
||||
nchk = ip_fast_csum((u8 *)lro->iph, ip->ihl);
|
||||
ip->check = nchk;
|
||||
|
||||
/* Update L4 header */
|
||||
tcp->ack_seq = lro->tcp_ack;
|
||||
tcp->window = lro->window;
|
||||
|
||||
/* Update tsecr field if this session has timestamps enabled */
|
||||
if (lro->saw_ts) {
|
||||
u32 *ptr = (u32 *)(tcp + 1);
|
||||
*(ptr+2) = lro->cur_tsecr;
|
||||
}
|
||||
|
||||
/* Update counters required for calculation of
|
||||
* average no. of packets aggregated.
|
||||
*/
|
||||
statinfo->sw_stat.sum_avg_pkts_aggregated += lro->sg_num;
|
||||
statinfo->sw_stat.num_aggregations++;
|
||||
}
|
||||
|
||||
static void aggregate_new_rx(lro_t *lro, struct iphdr *ip,
|
||||
struct tcphdr *tcp, u32 l4_pyld)
|
||||
{
|
||||
DBG_PRINT(INFO_DBG,"%s: Been here...\n", __FUNCTION__);
|
||||
lro->total_len += l4_pyld;
|
||||
lro->frags_len += l4_pyld;
|
||||
lro->tcp_next_seq += l4_pyld;
|
||||
lro->sg_num++;
|
||||
|
||||
/* Update ack seq no. and window ad(from this pkt) in LRO object */
|
||||
lro->tcp_ack = tcp->ack_seq;
|
||||
lro->window = tcp->window;
|
||||
|
||||
if (lro->saw_ts) {
|
||||
u32 *ptr;
|
||||
/* Update tsecr and tsval from this packet */
|
||||
ptr = (u32 *) (tcp + 1);
|
||||
lro->cur_tsval = *(ptr + 1);
|
||||
lro->cur_tsecr = *(ptr + 2);
|
||||
}
|
||||
}
|
||||
|
||||
static int verify_l3_l4_lro_capable(lro_t *l_lro, struct iphdr *ip,
|
||||
struct tcphdr *tcp, u32 tcp_pyld_len)
|
||||
{
|
||||
u8 *ptr;
|
||||
|
||||
DBG_PRINT(INFO_DBG,"%s: Been here...\n", __FUNCTION__);
|
||||
|
||||
if (!tcp_pyld_len) {
|
||||
/* Runt frame or a pure ack */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ip->ihl != 5) /* IP has options */
|
||||
return -1;
|
||||
|
||||
if (tcp->urg || tcp->psh || tcp->rst || tcp->syn || tcp->fin ||
|
||||
!tcp->ack) {
|
||||
/*
|
||||
* Currently recognize only the ack control word and
|
||||
* any other control field being set would result in
|
||||
* flushing the LRO session
|
||||
*/
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow only one TCP timestamp option. Don't aggregate if
|
||||
* any other options are detected.
|
||||
*/
|
||||
if (tcp->doff != 5 && tcp->doff != 8)
|
||||
return -1;
|
||||
|
||||
if (tcp->doff == 8) {
|
||||
ptr = (u8 *)(tcp + 1);
|
||||
while (*ptr == TCPOPT_NOP)
|
||||
ptr++;
|
||||
if (*ptr != TCPOPT_TIMESTAMP || *(ptr+1) != TCPOLEN_TIMESTAMP)
|
||||
return -1;
|
||||
|
||||
/* Ensure timestamp value increases monotonically */
|
||||
if (l_lro)
|
||||
if (l_lro->cur_tsval > *((u32 *)(ptr+2)))
|
||||
return -1;
|
||||
|
||||
/* timestamp echo reply should be non-zero */
|
||||
if (*((u32 *)(ptr+6)) == 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
s2io_club_tcp_session(u8 *buffer, u8 **tcp, u32 *tcp_len, lro_t **lro,
|
||||
RxD_t *rxdp, nic_t *sp)
|
||||
{
|
||||
struct iphdr *ip;
|
||||
struct tcphdr *tcph;
|
||||
int ret = 0, i;
|
||||
|
||||
if (!(ret = check_L2_lro_capable(buffer, &ip, (struct tcphdr **)tcp,
|
||||
rxdp))) {
|
||||
DBG_PRINT(INFO_DBG,"IP Saddr: %x Daddr: %x\n",
|
||||
ip->saddr, ip->daddr);
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
|
||||
tcph = (struct tcphdr *)*tcp;
|
||||
*tcp_len = get_l4_pyld_length(ip, tcph);
|
||||
for (i=0; i<MAX_LRO_SESSIONS; i++) {
|
||||
lro_t *l_lro = &sp->lro0_n[i];
|
||||
if (l_lro->in_use) {
|
||||
if (check_for_socket_match(l_lro, ip, tcph))
|
||||
continue;
|
||||
/* Sock pair matched */
|
||||
*lro = l_lro;
|
||||
|
||||
if ((*lro)->tcp_next_seq != ntohl(tcph->seq)) {
|
||||
DBG_PRINT(INFO_DBG, "%s:Out of order. expected "
|
||||
"0x%x, actual 0x%x\n", __FUNCTION__,
|
||||
(*lro)->tcp_next_seq,
|
||||
ntohl(tcph->seq));
|
||||
|
||||
sp->mac_control.stats_info->
|
||||
sw_stat.outof_sequence_pkts++;
|
||||
ret = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!verify_l3_l4_lro_capable(l_lro, ip, tcph,*tcp_len))
|
||||
ret = 1; /* Aggregate */
|
||||
else
|
||||
ret = 2; /* Flush both */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
/* Before searching for available LRO objects,
|
||||
* check if the pkt is L3/L4 aggregatable. If not
|
||||
* don't create new LRO session. Just send this
|
||||
* packet up.
|
||||
*/
|
||||
if (verify_l3_l4_lro_capable(NULL, ip, tcph, *tcp_len)) {
|
||||
return 5;
|
||||
}
|
||||
|
||||
for (i=0; i<MAX_LRO_SESSIONS; i++) {
|
||||
lro_t *l_lro = &sp->lro0_n[i];
|
||||
if (!(l_lro->in_use)) {
|
||||
*lro = l_lro;
|
||||
ret = 3; /* Begin anew */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) { /* sessions exceeded */
|
||||
DBG_PRINT(INFO_DBG,"%s:All LRO sessions already in use\n",
|
||||
__FUNCTION__);
|
||||
*lro = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (ret) {
|
||||
case 3:
|
||||
initiate_new_session(*lro, buffer, ip, tcph, *tcp_len);
|
||||
break;
|
||||
case 2:
|
||||
update_L3L4_header(sp, *lro);
|
||||
break;
|
||||
case 1:
|
||||
aggregate_new_rx(*lro, ip, tcph, *tcp_len);
|
||||
if ((*lro)->sg_num == sp->lro_max_aggr_per_sess) {
|
||||
update_L3L4_header(sp, *lro);
|
||||
ret = 4; /* Flush the LRO */
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DBG_PRINT(ERR_DBG,"%s:Dont know, can't say!!\n",
|
||||
__FUNCTION__);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void clear_lro_session(lro_t *lro)
|
||||
{
|
||||
static u16 lro_struct_size = sizeof(lro_t);
|
||||
|
||||
memset(lro, 0, lro_struct_size);
|
||||
}
|
||||
|
||||
static void queue_rx_frame(struct sk_buff *skb)
|
||||
{
|
||||
struct net_device *dev = skb->dev;
|
||||
|
||||
skb->protocol = eth_type_trans(skb, dev);
|
||||
#ifdef CONFIG_S2IO_NAPI
|
||||
netif_receive_skb(skb);
|
||||
#else
|
||||
netif_rx(skb);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void lro_append_pkt(nic_t *sp, lro_t *lro, struct sk_buff *skb,
|
||||
u32 tcp_len)
|
||||
{
|
||||
struct sk_buff *tmp, *first = lro->parent;
|
||||
|
||||
first->len += tcp_len;
|
||||
first->data_len = lro->frags_len;
|
||||
skb_pull(skb, (skb->len - tcp_len));
|
||||
if ((tmp = skb_shinfo(first)->frag_list)) {
|
||||
while (tmp->next)
|
||||
tmp = tmp->next;
|
||||
tmp->next = skb;
|
||||
}
|
||||
else
|
||||
skb_shinfo(first)->frag_list = skb;
|
||||
sp->mac_control.stats_info->sw_stat.clubbed_frms_cnt++;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -78,6 +78,13 @@ static int debug_level = ERR_DBG;
|
|||
typedef struct {
|
||||
unsigned long long single_ecc_errs;
|
||||
unsigned long long double_ecc_errs;
|
||||
/* LRO statistics */
|
||||
unsigned long long clubbed_frms_cnt;
|
||||
unsigned long long sending_both;
|
||||
unsigned long long outof_sequence_pkts;
|
||||
unsigned long long flush_max_pkts;
|
||||
unsigned long long sum_avg_pkts_aggregated;
|
||||
unsigned long long num_aggregations;
|
||||
} swStat_t;
|
||||
|
||||
/* The statistics block of Xena */
|
||||
|
@ -680,6 +687,24 @@ struct msix_info_st {
|
|||
u64 data;
|
||||
};
|
||||
|
||||
/* Data structure to represent a LRO session */
|
||||
typedef struct lro {
|
||||
struct sk_buff *parent;
|
||||
u8 *l2h;
|
||||
struct iphdr *iph;
|
||||
struct tcphdr *tcph;
|
||||
u32 tcp_next_seq;
|
||||
u32 tcp_ack;
|
||||
int total_len;
|
||||
int frags_len;
|
||||
int sg_num;
|
||||
int in_use;
|
||||
u16 window;
|
||||
u32 cur_tsval;
|
||||
u32 cur_tsecr;
|
||||
u8 saw_ts;
|
||||
}lro_t;
|
||||
|
||||
/* Structure representing one instance of the NIC */
|
||||
struct s2io_nic {
|
||||
int rxd_mode;
|
||||
|
@ -784,6 +809,13 @@ struct s2io_nic {
|
|||
#define XFRAME_II_DEVICE 2
|
||||
u8 device_type;
|
||||
|
||||
#define MAX_LRO_SESSIONS 32
|
||||
lro_t lro0_n[MAX_LRO_SESSIONS];
|
||||
unsigned long clubbed_frms_cnt;
|
||||
unsigned long sending_both;
|
||||
u8 lro;
|
||||
u16 lro_max_aggr_per_sess;
|
||||
|
||||
#define INTA 0
|
||||
#define MSI 1
|
||||
#define MSI_X 2
|
||||
|
@ -937,4 +969,10 @@ static void s2io_card_down(nic_t *nic);
|
|||
static int s2io_card_up(nic_t *nic);
|
||||
static int get_xena_rev_id(struct pci_dev *pdev);
|
||||
static void restore_xmsi_data(nic_t *nic);
|
||||
|
||||
static int s2io_club_tcp_session(u8 *buffer, u8 **tcp, u32 *tcp_len, lro_t **lro, RxD_t *rxdp, nic_t *sp);
|
||||
static void clear_lro_session(lro_t *lro);
|
||||
static void queue_rx_frame(struct sk_buff *skb);
|
||||
static void update_L3L4_header(nic_t *sp, lro_t *lro);
|
||||
static void lro_append_pkt(nic_t *sp, lro_t *lro, struct sk_buff *skb, u32 tcp_len);
|
||||
#endif /* _S2IO_H */
|
||||
|
|
Loading…
Reference in New Issue