diff options
author | Jeff Garzik <jeff@garzik.org> | 2006-03-02 14:26:30 -0500 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2006-03-02 14:26:30 -0500 |
commit | 2ade43618b0aee83a50b344171d33d85c73d01b1 (patch) | |
tree | 0a49fe46159a6ef66098a4f37935412f4f226f4c /drivers/net/s2io.c | |
parent | 75e47b36004d136edff68295420424cba3a5ccd0 (diff) | |
parent | 79dc190147f8a87718fe72928d5ceb58e09acdb9 (diff) |
Merge branch 'lro'
Diffstat (limited to 'drivers/net/s2io.c')
-rw-r--r-- | drivers/net/s2io.c | 589 |
1 files changed, 527 insertions, 62 deletions
diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index da0d8a85acce..0db218c2dbeb 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c | |||
@@ -57,16 +57,20 @@ | |||
57 | #include <linux/ethtool.h> | 57 | #include <linux/ethtool.h> |
58 | #include <linux/workqueue.h> | 58 | #include <linux/workqueue.h> |
59 | #include <linux/if_vlan.h> | 59 | #include <linux/if_vlan.h> |
60 | #include <linux/ip.h> | ||
61 | #include <linux/tcp.h> | ||
62 | #include <net/tcp.h> | ||
60 | 63 | ||
61 | #include <asm/system.h> | 64 | #include <asm/system.h> |
62 | #include <asm/uaccess.h> | 65 | #include <asm/uaccess.h> |
63 | #include <asm/io.h> | 66 | #include <asm/io.h> |
67 | #include <asm/div64.h> | ||
64 | 68 | ||
65 | /* local include */ | 69 | /* local include */ |
66 | #include "s2io.h" | 70 | #include "s2io.h" |
67 | #include "s2io-regs.h" | 71 | #include "s2io-regs.h" |
68 | 72 | ||
69 | #define DRV_VERSION "Version 2.0.9.4" | 73 | #define DRV_VERSION "2.0.11.2" |
70 | 74 | ||
71 | /* S2io Driver name & version. */ | 75 | /* S2io Driver name & version. */ |
72 | static char s2io_driver_name[] = "Neterion"; | 76 | static char s2io_driver_name[] = "Neterion"; |
@@ -168,6 +172,11 @@ static char ethtool_stats_keys[][ETH_GSTRING_LEN] = { | |||
168 | {"\n DRIVER STATISTICS"}, | 172 | {"\n DRIVER STATISTICS"}, |
169 | {"single_bit_ecc_errs"}, | 173 | {"single_bit_ecc_errs"}, |
170 | {"double_bit_ecc_errs"}, | 174 | {"double_bit_ecc_errs"}, |
175 | ("lro_aggregated_pkts"), | ||
176 | ("lro_flush_both_count"), | ||
177 | ("lro_out_of_sequence_pkts"), | ||
178 | ("lro_flush_due_to_max_pkts"), | ||
179 | ("lro_avg_aggr_pkts"), | ||
171 | }; | 180 | }; |
172 | 181 | ||
173 | #define S2IO_STAT_LEN sizeof(ethtool_stats_keys)/ ETH_GSTRING_LEN | 182 | #define S2IO_STAT_LEN sizeof(ethtool_stats_keys)/ ETH_GSTRING_LEN |
@@ -317,6 +326,12 @@ static unsigned int indicate_max_pkts; | |||
317 | static unsigned int rxsync_frequency = 3; | 326 | static unsigned int rxsync_frequency = 3; |
318 | /* Interrupt type. Values can be 0(INTA), 1(MSI), 2(MSI_X) */ | 327 | /* Interrupt type. Values can be 0(INTA), 1(MSI), 2(MSI_X) */ |
319 | static unsigned int intr_type = 0; | 328 | static unsigned int intr_type = 0; |
329 | /* Large receive offload feature */ | ||
330 | static unsigned int lro = 0; | ||
331 | /* Max pkts to be aggregated by LRO at one time. If not specified, | ||
332 | * aggregation happens until we hit max IP pkt size(64K) | ||
333 | */ | ||
334 | static unsigned int lro_max_pkts = 0xFFFF; | ||
320 | 335 | ||
321 | /* | 336 | /* |
322 | * S2IO device table. | 337 | * S2IO device table. |
@@ -1476,6 +1491,19 @@ static int init_nic(struct s2io_nic *nic) | |||
1476 | writel((u32) (val64 >> 32), (add + 4)); | 1491 | writel((u32) (val64 >> 32), (add + 4)); |
1477 | val64 = readq(&bar0->mac_cfg); | 1492 | val64 = readq(&bar0->mac_cfg); |
1478 | 1493 | ||
1494 | /* Enable FCS stripping by adapter */ | ||
1495 | add = &bar0->mac_cfg; | ||
1496 | val64 = readq(&bar0->mac_cfg); | ||
1497 | val64 |= MAC_CFG_RMAC_STRIP_FCS; | ||
1498 | if (nic->device_type == XFRAME_II_DEVICE) | ||
1499 | writeq(val64, &bar0->mac_cfg); | ||
1500 | else { | ||
1501 | writeq(RMAC_CFG_KEY(0x4C0D), &bar0->rmac_cfg_key); | ||
1502 | writel((u32) (val64), add); | ||
1503 | writeq(RMAC_CFG_KEY(0x4C0D), &bar0->rmac_cfg_key); | ||
1504 | writel((u32) (val64 >> 32), (add + 4)); | ||
1505 | } | ||
1506 | |||
1479 | /* | 1507 | /* |
1480 | * Set the time value to be inserted in the pause frame | 1508 | * Set the time value to be inserted in the pause frame |
1481 | * generated by xena. | 1509 | * generated by xena. |
@@ -2569,6 +2597,8 @@ static void rx_intr_handler(ring_info_t *ring_data) | |||
2569 | #ifndef CONFIG_S2IO_NAPI | 2597 | #ifndef CONFIG_S2IO_NAPI |
2570 | int pkt_cnt = 0; | 2598 | int pkt_cnt = 0; |
2571 | #endif | 2599 | #endif |
2600 | int i; | ||
2601 | |||
2572 | spin_lock(&nic->rx_lock); | 2602 | spin_lock(&nic->rx_lock); |
2573 | if (atomic_read(&nic->card_state) == CARD_DOWN) { | 2603 | if (atomic_read(&nic->card_state) == CARD_DOWN) { |
2574 | DBG_PRINT(INTR_DBG, "%s: %s going down for reset\n", | 2604 | 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) | |||
2661 | break; | 2691 | break; |
2662 | #endif | 2692 | #endif |
2663 | } | 2693 | } |
2694 | if (nic->lro) { | ||
2695 | /* Clear all LRO sessions before exiting */ | ||
2696 | for (i=0; i<MAX_LRO_SESSIONS; i++) { | ||
2697 | lro_t *lro = &nic->lro0_n[i]; | ||
2698 | if (lro->in_use) { | ||
2699 | update_L3L4_header(nic, lro); | ||
2700 | queue_rx_frame(lro->parent); | ||
2701 | clear_lro_session(lro); | ||
2702 | } | ||
2703 | } | ||
2704 | } | ||
2705 | |||
2664 | spin_unlock(&nic->rx_lock); | 2706 | spin_unlock(&nic->rx_lock); |
2665 | } | 2707 | } |
2666 | 2708 | ||
@@ -3668,23 +3710,32 @@ s2io_msi_handle(int irq, void *dev_id, struct pt_regs *regs) | |||
3668 | * else schedule a tasklet to reallocate the buffers. | 3710 | * else schedule a tasklet to reallocate the buffers. |
3669 | */ | 3711 | */ |
3670 | for (i = 0; i < config->rx_ring_num; i++) { | 3712 | for (i = 0; i < config->rx_ring_num; i++) { |
3671 | int rxb_size = atomic_read(&sp->rx_bufs_left[i]); | 3713 | if (!sp->lro) { |
3672 | int level = rx_buffer_level(sp, rxb_size, i); | 3714 | int rxb_size = atomic_read(&sp->rx_bufs_left[i]); |
3673 | 3715 | int level = rx_buffer_level(sp, rxb_size, i); | |
3674 | if ((level == PANIC) && (!TASKLET_IN_USE)) { | 3716 | |
3675 | DBG_PRINT(INTR_DBG, "%s: Rx BD hit ", dev->name); | 3717 | if ((level == PANIC) && (!TASKLET_IN_USE)) { |
3676 | DBG_PRINT(INTR_DBG, "PANIC levels\n"); | 3718 | DBG_PRINT(INTR_DBG, "%s: Rx BD hit ", |
3677 | if ((ret = fill_rx_buffers(sp, i)) == -ENOMEM) { | 3719 | dev->name); |
3678 | DBG_PRINT(ERR_DBG, "%s:Out of memory", | 3720 | DBG_PRINT(INTR_DBG, "PANIC levels\n"); |
3679 | dev->name); | 3721 | if ((ret = fill_rx_buffers(sp, i)) == -ENOMEM) { |
3680 | DBG_PRINT(ERR_DBG, " in ISR!!\n"); | 3722 | DBG_PRINT(ERR_DBG, "%s:Out of memory", |
3723 | dev->name); | ||
3724 | DBG_PRINT(ERR_DBG, " in ISR!!\n"); | ||
3725 | clear_bit(0, (&sp->tasklet_status)); | ||
3726 | atomic_dec(&sp->isr_cnt); | ||
3727 | return IRQ_HANDLED; | ||
3728 | } | ||
3681 | clear_bit(0, (&sp->tasklet_status)); | 3729 | clear_bit(0, (&sp->tasklet_status)); |
3682 | atomic_dec(&sp->isr_cnt); | 3730 | } else if (level == LOW) { |
3683 | return IRQ_HANDLED; | 3731 | tasklet_schedule(&sp->task); |
3684 | } | 3732 | } |
3685 | clear_bit(0, (&sp->tasklet_status)); | 3733 | } |
3686 | } else if (level == LOW) { | 3734 | else if (fill_rx_buffers(sp, i) == -ENOMEM) { |
3687 | tasklet_schedule(&sp->task); | 3735 | DBG_PRINT(ERR_DBG, "%s:Out of memory", |
3736 | dev->name); | ||
3737 | DBG_PRINT(ERR_DBG, " in Rx Intr!!\n"); | ||
3738 | break; | ||
3688 | } | 3739 | } |
3689 | } | 3740 | } |
3690 | 3741 | ||
@@ -3697,29 +3748,37 @@ s2io_msix_ring_handle(int irq, void *dev_id, struct pt_regs *regs) | |||
3697 | { | 3748 | { |
3698 | ring_info_t *ring = (ring_info_t *)dev_id; | 3749 | ring_info_t *ring = (ring_info_t *)dev_id; |
3699 | nic_t *sp = ring->nic; | 3750 | nic_t *sp = ring->nic; |
3751 | struct net_device *dev = (struct net_device *) dev_id; | ||
3700 | int rxb_size, level, rng_n; | 3752 | int rxb_size, level, rng_n; |
3701 | 3753 | ||
3702 | atomic_inc(&sp->isr_cnt); | 3754 | atomic_inc(&sp->isr_cnt); |
3703 | rx_intr_handler(ring); | 3755 | rx_intr_handler(ring); |
3704 | 3756 | ||
3705 | rng_n = ring->ring_no; | 3757 | rng_n = ring->ring_no; |
3706 | rxb_size = atomic_read(&sp->rx_bufs_left[rng_n]); | 3758 | if (!sp->lro) { |
3707 | level = rx_buffer_level(sp, rxb_size, rng_n); | 3759 | rxb_size = atomic_read(&sp->rx_bufs_left[rng_n]); |
3708 | 3760 | level = rx_buffer_level(sp, rxb_size, rng_n); | |
3709 | if ((level == PANIC) && (!TASKLET_IN_USE)) { | 3761 | |
3710 | int ret; | 3762 | if ((level == PANIC) && (!TASKLET_IN_USE)) { |
3711 | DBG_PRINT(INTR_DBG, "%s: Rx BD hit ", __FUNCTION__); | 3763 | int ret; |
3712 | DBG_PRINT(INTR_DBG, "PANIC levels\n"); | 3764 | DBG_PRINT(INTR_DBG, "%s: Rx BD hit ", __FUNCTION__); |
3713 | if ((ret = fill_rx_buffers(sp, rng_n)) == -ENOMEM) { | 3765 | DBG_PRINT(INTR_DBG, "PANIC levels\n"); |
3714 | DBG_PRINT(ERR_DBG, "Out of memory in %s", | 3766 | if ((ret = fill_rx_buffers(sp, rng_n)) == -ENOMEM) { |
3715 | __FUNCTION__); | 3767 | DBG_PRINT(ERR_DBG, "Out of memory in %s", |
3768 | __FUNCTION__); | ||
3769 | clear_bit(0, (&sp->tasklet_status)); | ||
3770 | return IRQ_HANDLED; | ||
3771 | } | ||
3716 | clear_bit(0, (&sp->tasklet_status)); | 3772 | clear_bit(0, (&sp->tasklet_status)); |
3717 | return IRQ_HANDLED; | 3773 | } else if (level == LOW) { |
3774 | tasklet_schedule(&sp->task); | ||
3718 | } | 3775 | } |
3719 | clear_bit(0, (&sp->tasklet_status)); | ||
3720 | } else if (level == LOW) { | ||
3721 | tasklet_schedule(&sp->task); | ||
3722 | } | 3776 | } |
3777 | else if (fill_rx_buffers(sp, rng_n) == -ENOMEM) { | ||
3778 | DBG_PRINT(ERR_DBG, "%s:Out of memory", dev->name); | ||
3779 | DBG_PRINT(ERR_DBG, " in Rx Intr!!\n"); | ||
3780 | } | ||
3781 | |||
3723 | atomic_dec(&sp->isr_cnt); | 3782 | atomic_dec(&sp->isr_cnt); |
3724 | 3783 | ||
3725 | return IRQ_HANDLED; | 3784 | return IRQ_HANDLED; |
@@ -3875,24 +3934,33 @@ static irqreturn_t s2io_isr(int irq, void *dev_id, struct pt_regs *regs) | |||
3875 | */ | 3934 | */ |
3876 | #ifndef CONFIG_S2IO_NAPI | 3935 | #ifndef CONFIG_S2IO_NAPI |
3877 | for (i = 0; i < config->rx_ring_num; i++) { | 3936 | for (i = 0; i < config->rx_ring_num; i++) { |
3878 | int ret; | 3937 | if (!sp->lro) { |
3879 | int rxb_size = atomic_read(&sp->rx_bufs_left[i]); | 3938 | int ret; |
3880 | int level = rx_buffer_level(sp, rxb_size, i); | 3939 | int rxb_size = atomic_read(&sp->rx_bufs_left[i]); |
3881 | 3940 | int level = rx_buffer_level(sp, rxb_size, i); | |
3882 | if ((level == PANIC) && (!TASKLET_IN_USE)) { | 3941 | |
3883 | DBG_PRINT(INTR_DBG, "%s: Rx BD hit ", dev->name); | 3942 | if ((level == PANIC) && (!TASKLET_IN_USE)) { |
3884 | DBG_PRINT(INTR_DBG, "PANIC levels\n"); | 3943 | DBG_PRINT(INTR_DBG, "%s: Rx BD hit ", |
3885 | if ((ret = fill_rx_buffers(sp, i)) == -ENOMEM) { | 3944 | dev->name); |
3886 | DBG_PRINT(ERR_DBG, "%s:Out of memory", | 3945 | DBG_PRINT(INTR_DBG, "PANIC levels\n"); |
3887 | dev->name); | 3946 | if ((ret = fill_rx_buffers(sp, i)) == -ENOMEM) { |
3888 | DBG_PRINT(ERR_DBG, " in ISR!!\n"); | 3947 | DBG_PRINT(ERR_DBG, "%s:Out of memory", |
3948 | dev->name); | ||
3949 | DBG_PRINT(ERR_DBG, " in ISR!!\n"); | ||
3950 | clear_bit(0, (&sp->tasklet_status)); | ||
3951 | atomic_dec(&sp->isr_cnt); | ||
3952 | return IRQ_HANDLED; | ||
3953 | } | ||
3889 | clear_bit(0, (&sp->tasklet_status)); | 3954 | clear_bit(0, (&sp->tasklet_status)); |
3890 | atomic_dec(&sp->isr_cnt); | 3955 | } else if (level == LOW) { |
3891 | return IRQ_HANDLED; | 3956 | tasklet_schedule(&sp->task); |
3892 | } | 3957 | } |
3893 | clear_bit(0, (&sp->tasklet_status)); | 3958 | } |
3894 | } else if (level == LOW) { | 3959 | else if (fill_rx_buffers(sp, i) == -ENOMEM) { |
3895 | tasklet_schedule(&sp->task); | 3960 | DBG_PRINT(ERR_DBG, "%s:Out of memory", |
3961 | dev->name); | ||
3962 | DBG_PRINT(ERR_DBG, " in Rx intr!!\n"); | ||
3963 | break; | ||
3896 | } | 3964 | } |
3897 | } | 3965 | } |
3898 | #endif | 3966 | #endif |
@@ -5043,6 +5111,7 @@ static void s2io_get_ethtool_stats(struct net_device *dev, | |||
5043 | int i = 0; | 5111 | int i = 0; |
5044 | nic_t *sp = dev->priv; | 5112 | nic_t *sp = dev->priv; |
5045 | StatInfo_t *stat_info = sp->mac_control.stats_info; | 5113 | StatInfo_t *stat_info = sp->mac_control.stats_info; |
5114 | u64 tmp; | ||
5046 | 5115 | ||
5047 | s2io_updt_stats(sp); | 5116 | s2io_updt_stats(sp); |
5048 | tmp_stats[i++] = | 5117 | tmp_stats[i++] = |
@@ -5134,6 +5203,16 @@ static void s2io_get_ethtool_stats(struct net_device *dev, | |||
5134 | tmp_stats[i++] = 0; | 5203 | tmp_stats[i++] = 0; |
5135 | tmp_stats[i++] = stat_info->sw_stat.single_ecc_errs; | 5204 | tmp_stats[i++] = stat_info->sw_stat.single_ecc_errs; |
5136 | tmp_stats[i++] = stat_info->sw_stat.double_ecc_errs; | 5205 | tmp_stats[i++] = stat_info->sw_stat.double_ecc_errs; |
5206 | tmp_stats[i++] = stat_info->sw_stat.clubbed_frms_cnt; | ||
5207 | tmp_stats[i++] = stat_info->sw_stat.sending_both; | ||
5208 | tmp_stats[i++] = stat_info->sw_stat.outof_sequence_pkts; | ||
5209 | tmp_stats[i++] = stat_info->sw_stat.flush_max_pkts; | ||
5210 | tmp = 0; | ||
5211 | if (stat_info->sw_stat.num_aggregations) { | ||
5212 | tmp = stat_info->sw_stat.sum_avg_pkts_aggregated; | ||
5213 | do_div(tmp, stat_info->sw_stat.num_aggregations); | ||
5214 | } | ||
5215 | tmp_stats[i++] = tmp; | ||
5137 | } | 5216 | } |
5138 | 5217 | ||
5139 | static int s2io_ethtool_get_regs_len(struct net_device *dev) | 5218 | static int s2io_ethtool_get_regs_len(struct net_device *dev) |
@@ -5515,6 +5594,14 @@ static int s2io_card_up(nic_t * sp) | |||
5515 | /* Setting its receive mode */ | 5594 | /* Setting its receive mode */ |
5516 | s2io_set_multicast(dev); | 5595 | s2io_set_multicast(dev); |
5517 | 5596 | ||
5597 | if (sp->lro) { | ||
5598 | /* Initialize max aggregatable pkts based on MTU */ | ||
5599 | sp->lro_max_aggr_per_sess = ((1<<16) - 1) / dev->mtu; | ||
5600 | /* Check if we can use(if specified) user provided value */ | ||
5601 | if (lro_max_pkts < sp->lro_max_aggr_per_sess) | ||
5602 | sp->lro_max_aggr_per_sess = lro_max_pkts; | ||
5603 | } | ||
5604 | |||
5518 | /* Enable tasklet for the device */ | 5605 | /* Enable tasklet for the device */ |
5519 | tasklet_init(&sp->task, s2io_tasklet, (unsigned long) dev); | 5606 | tasklet_init(&sp->task, s2io_tasklet, (unsigned long) dev); |
5520 | 5607 | ||
@@ -5607,6 +5694,7 @@ static int rx_osm_handler(ring_info_t *ring_data, RxD_t * rxdp) | |||
5607 | ((unsigned long) rxdp->Host_Control); | 5694 | ((unsigned long) rxdp->Host_Control); |
5608 | int ring_no = ring_data->ring_no; | 5695 | int ring_no = ring_data->ring_no; |
5609 | u16 l3_csum, l4_csum; | 5696 | u16 l3_csum, l4_csum; |
5697 | lro_t *lro; | ||
5610 | 5698 | ||
5611 | skb->dev = dev; | 5699 | skb->dev = dev; |
5612 | if (rxdp->Control_1 & RXD_T_CODE) { | 5700 | if (rxdp->Control_1 & RXD_T_CODE) { |
@@ -5655,7 +5743,8 @@ static int rx_osm_handler(ring_info_t *ring_data, RxD_t * rxdp) | |||
5655 | skb_put(skb, buf2_len); | 5743 | skb_put(skb, buf2_len); |
5656 | } | 5744 | } |
5657 | 5745 | ||
5658 | if ((rxdp->Control_1 & TCP_OR_UDP_FRAME) && | 5746 | if ((rxdp->Control_1 & TCP_OR_UDP_FRAME) && ((!sp->lro) || |
5747 | (sp->lro && (!(rxdp->Control_1 & RXD_FRAME_IP_FRAG)))) && | ||
5659 | (sp->rx_csum)) { | 5748 | (sp->rx_csum)) { |
5660 | l3_csum = RXD_GET_L3_CKSUM(rxdp->Control_1); | 5749 | l3_csum = RXD_GET_L3_CKSUM(rxdp->Control_1); |
5661 | l4_csum = RXD_GET_L4_CKSUM(rxdp->Control_1); | 5750 | 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) | |||
5666 | * a flag in the RxD. | 5755 | * a flag in the RxD. |
5667 | */ | 5756 | */ |
5668 | skb->ip_summed = CHECKSUM_UNNECESSARY; | 5757 | skb->ip_summed = CHECKSUM_UNNECESSARY; |
5758 | if (sp->lro) { | ||
5759 | u32 tcp_len; | ||
5760 | u8 *tcp; | ||
5761 | int ret = 0; | ||
5762 | |||
5763 | ret = s2io_club_tcp_session(skb->data, &tcp, | ||
5764 | &tcp_len, &lro, rxdp, sp); | ||
5765 | switch (ret) { | ||
5766 | case 3: /* Begin anew */ | ||
5767 | lro->parent = skb; | ||
5768 | goto aggregate; | ||
5769 | case 1: /* Aggregate */ | ||
5770 | { | ||
5771 | lro_append_pkt(sp, lro, | ||
5772 | skb, tcp_len); | ||
5773 | goto aggregate; | ||
5774 | } | ||
5775 | case 4: /* Flush session */ | ||
5776 | { | ||
5777 | lro_append_pkt(sp, lro, | ||
5778 | skb, tcp_len); | ||
5779 | queue_rx_frame(lro->parent); | ||
5780 | clear_lro_session(lro); | ||
5781 | sp->mac_control.stats_info-> | ||
5782 | sw_stat.flush_max_pkts++; | ||
5783 | goto aggregate; | ||
5784 | } | ||
5785 | case 2: /* Flush both */ | ||
5786 | lro->parent->data_len = | ||
5787 | lro->frags_len; | ||
5788 | sp->mac_control.stats_info-> | ||
5789 | sw_stat.sending_both++; | ||
5790 | queue_rx_frame(lro->parent); | ||
5791 | clear_lro_session(lro); | ||
5792 | goto send_up; | ||
5793 | case 0: /* sessions exceeded */ | ||
5794 | case 5: /* | ||
5795 | * First pkt in session not | ||
5796 | * L3/L4 aggregatable | ||
5797 | */ | ||
5798 | break; | ||
5799 | default: | ||
5800 | DBG_PRINT(ERR_DBG, | ||
5801 | "%s: Samadhana!!\n", | ||
5802 | __FUNCTION__); | ||
5803 | BUG(); | ||
5804 | } | ||
5805 | } | ||
5669 | } else { | 5806 | } else { |
5670 | /* | 5807 | /* |
5671 | * Packet with erroneous checksum, let the | 5808 | * Packet with erroneous checksum, let the |
@@ -5677,25 +5814,31 @@ static int rx_osm_handler(ring_info_t *ring_data, RxD_t * rxdp) | |||
5677 | skb->ip_summed = CHECKSUM_NONE; | 5814 | skb->ip_summed = CHECKSUM_NONE; |
5678 | } | 5815 | } |
5679 | 5816 | ||
5680 | skb->protocol = eth_type_trans(skb, dev); | 5817 | if (!sp->lro) { |
5818 | skb->protocol = eth_type_trans(skb, dev); | ||
5681 | #ifdef CONFIG_S2IO_NAPI | 5819 | #ifdef CONFIG_S2IO_NAPI |
5682 | if (sp->vlgrp && RXD_GET_VLAN_TAG(rxdp->Control_2)) { | 5820 | if (sp->vlgrp && RXD_GET_VLAN_TAG(rxdp->Control_2)) { |
5683 | /* Queueing the vlan frame to the upper layer */ | 5821 | /* Queueing the vlan frame to the upper layer */ |
5684 | vlan_hwaccel_receive_skb(skb, sp->vlgrp, | 5822 | vlan_hwaccel_receive_skb(skb, sp->vlgrp, |
5685 | RXD_GET_VLAN_TAG(rxdp->Control_2)); | 5823 | RXD_GET_VLAN_TAG(rxdp->Control_2)); |
5686 | } else { | 5824 | } else { |
5687 | netif_receive_skb(skb); | 5825 | netif_receive_skb(skb); |
5688 | } | 5826 | } |
5689 | #else | 5827 | #else |
5690 | if (sp->vlgrp && RXD_GET_VLAN_TAG(rxdp->Control_2)) { | 5828 | if (sp->vlgrp && RXD_GET_VLAN_TAG(rxdp->Control_2)) { |
5691 | /* Queueing the vlan frame to the upper layer */ | 5829 | /* Queueing the vlan frame to the upper layer */ |
5692 | vlan_hwaccel_rx(skb, sp->vlgrp, | 5830 | vlan_hwaccel_rx(skb, sp->vlgrp, |
5693 | RXD_GET_VLAN_TAG(rxdp->Control_2)); | 5831 | RXD_GET_VLAN_TAG(rxdp->Control_2)); |
5694 | } else { | 5832 | } else { |
5695 | netif_rx(skb); | 5833 | netif_rx(skb); |
5696 | } | 5834 | } |
5697 | #endif | 5835 | #endif |
5836 | } else { | ||
5837 | send_up: | ||
5838 | queue_rx_frame(skb); | ||
5839 | } | ||
5698 | dev->last_rx = jiffies; | 5840 | dev->last_rx = jiffies; |
5841 | aggregate: | ||
5699 | atomic_dec(&sp->rx_bufs_left[ring_no]); | 5842 | atomic_dec(&sp->rx_bufs_left[ring_no]); |
5700 | return SUCCESS; | 5843 | return SUCCESS; |
5701 | } | 5844 | } |
@@ -5807,6 +5950,8 @@ module_param(indicate_max_pkts, int, 0); | |||
5807 | #endif | 5950 | #endif |
5808 | module_param(rxsync_frequency, int, 0); | 5951 | module_param(rxsync_frequency, int, 0); |
5809 | module_param(intr_type, int, 0); | 5952 | module_param(intr_type, int, 0); |
5953 | module_param(lro, int, 0); | ||
5954 | module_param(lro_max_pkts, int, 0); | ||
5810 | 5955 | ||
5811 | /** | 5956 | /** |
5812 | * s2io_init_nic - Initialization of the adapter . | 5957 | * s2io_init_nic - Initialization of the adapter . |
@@ -5938,6 +6083,7 @@ Defaulting to INTA\n"); | |||
5938 | else | 6083 | else |
5939 | sp->device_type = XFRAME_I_DEVICE; | 6084 | sp->device_type = XFRAME_I_DEVICE; |
5940 | 6085 | ||
6086 | sp->lro = lro; | ||
5941 | 6087 | ||
5942 | /* Initialize some PCI/PCI-X fields of the NIC. */ | 6088 | /* Initialize some PCI/PCI-X fields of the NIC. */ |
5943 | s2io_init_pci(sp); | 6089 | s2io_init_pci(sp); |
@@ -6241,6 +6387,10 @@ Defaulting to INTA\n"); | |||
6241 | DBG_PRINT(ERR_DBG, "%s: 3-Buffer mode support has been " | 6387 | DBG_PRINT(ERR_DBG, "%s: 3-Buffer mode support has been " |
6242 | "enabled\n",dev->name); | 6388 | "enabled\n",dev->name); |
6243 | 6389 | ||
6390 | if (sp->lro) | ||
6391 | DBG_PRINT(ERR_DBG, "%s: Large receive offload enabled\n", | ||
6392 | dev->name); | ||
6393 | |||
6244 | /* Initialize device name */ | 6394 | /* Initialize device name */ |
6245 | strcpy(sp->name, dev->name); | 6395 | strcpy(sp->name, dev->name); |
6246 | if (sp->device_type & XFRAME_II_DEVICE) | 6396 | if (sp->device_type & XFRAME_II_DEVICE) |
@@ -6351,3 +6501,318 @@ static void s2io_closer(void) | |||
6351 | 6501 | ||
6352 | module_init(s2io_starter); | 6502 | module_init(s2io_starter); |
6353 | module_exit(s2io_closer); | 6503 | module_exit(s2io_closer); |
6504 | |||
6505 | static int check_L2_lro_capable(u8 *buffer, struct iphdr **ip, | ||
6506 | struct tcphdr **tcp, RxD_t *rxdp) | ||
6507 | { | ||
6508 | int ip_off; | ||
6509 | u8 l2_type = (u8)((rxdp->Control_1 >> 37) & 0x7), ip_len; | ||
6510 | |||
6511 | if (!(rxdp->Control_1 & RXD_FRAME_PROTO_TCP)) { | ||
6512 | DBG_PRINT(INIT_DBG,"%s: Non-TCP frames not supported for LRO\n", | ||
6513 | __FUNCTION__); | ||
6514 | return -1; | ||
6515 | } | ||
6516 | |||
6517 | /* TODO: | ||
6518 | * By default the VLAN field in the MAC is stripped by the card, if this | ||
6519 | * feature is turned off in rx_pa_cfg register, then the ip_off field | ||
6520 | * has to be shifted by a further 2 bytes | ||
6521 | */ | ||
6522 | switch (l2_type) { | ||
6523 | case 0: /* DIX type */ | ||
6524 | case 4: /* DIX type with VLAN */ | ||
6525 | ip_off = HEADER_ETHERNET_II_802_3_SIZE; | ||
6526 | break; | ||
6527 | /* LLC, SNAP etc are considered non-mergeable */ | ||
6528 | default: | ||
6529 | return -1; | ||
6530 | } | ||
6531 | |||
6532 | *ip = (struct iphdr *)((u8 *)buffer + ip_off); | ||
6533 | ip_len = (u8)((*ip)->ihl); | ||
6534 | ip_len <<= 2; | ||
6535 | *tcp = (struct tcphdr *)((unsigned long)*ip + ip_len); | ||
6536 | |||
6537 | return 0; | ||
6538 | } | ||
6539 | |||
6540 | static int check_for_socket_match(lro_t *lro, struct iphdr *ip, | ||
6541 | struct tcphdr *tcp) | ||
6542 | { | ||
6543 | DBG_PRINT(INFO_DBG,"%s: Been here...\n", __FUNCTION__); | ||
6544 | if ((lro->iph->saddr != ip->saddr) || (lro->iph->daddr != ip->daddr) || | ||
6545 | (lro->tcph->source != tcp->source) || (lro->tcph->dest != tcp->dest)) | ||
6546 | return -1; | ||
6547 | return 0; | ||
6548 | } | ||
6549 | |||
6550 | static inline int get_l4_pyld_length(struct iphdr *ip, struct tcphdr *tcp) | ||
6551 | { | ||
6552 | return(ntohs(ip->tot_len) - (ip->ihl << 2) - (tcp->doff << 2)); | ||
6553 | } | ||
6554 | |||
6555 | static void initiate_new_session(lro_t *lro, u8 *l2h, | ||
6556 | struct iphdr *ip, struct tcphdr *tcp, u32 tcp_pyld_len) | ||
6557 | { | ||
6558 | DBG_PRINT(INFO_DBG,"%s: Been here...\n", __FUNCTION__); | ||
6559 | lro->l2h = l2h; | ||
6560 | lro->iph = ip; | ||
6561 | lro->tcph = tcp; | ||
6562 | lro->tcp_next_seq = tcp_pyld_len + ntohl(tcp->seq); | ||
6563 | lro->tcp_ack = ntohl(tcp->ack_seq); | ||
6564 | lro->sg_num = 1; | ||
6565 | lro->total_len = ntohs(ip->tot_len); | ||
6566 | lro->frags_len = 0; | ||
6567 | /* | ||
6568 | * check if we saw TCP timestamp. Other consistency checks have | ||
6569 | * already been done. | ||
6570 | */ | ||
6571 | if (tcp->doff == 8) { | ||
6572 | u32 *ptr; | ||
6573 | ptr = (u32 *)(tcp+1); | ||
6574 | lro->saw_ts = 1; | ||
6575 | lro->cur_tsval = *(ptr+1); | ||
6576 | lro->cur_tsecr = *(ptr+2); | ||
6577 | } | ||
6578 | lro->in_use = 1; | ||
6579 | } | ||
6580 | |||
6581 | static void update_L3L4_header(nic_t *sp, lro_t *lro) | ||
6582 | { | ||
6583 | struct iphdr *ip = lro->iph; | ||
6584 | struct tcphdr *tcp = lro->tcph; | ||
6585 | u16 nchk; | ||
6586 | StatInfo_t *statinfo = sp->mac_control.stats_info; | ||
6587 | DBG_PRINT(INFO_DBG,"%s: Been here...\n", __FUNCTION__); | ||
6588 | |||
6589 | /* Update L3 header */ | ||
6590 | ip->tot_len = htons(lro->total_len); | ||
6591 | ip->check = 0; | ||
6592 | nchk = ip_fast_csum((u8 *)lro->iph, ip->ihl); | ||
6593 | ip->check = nchk; | ||
6594 | |||
6595 | /* Update L4 header */ | ||
6596 | tcp->ack_seq = lro->tcp_ack; | ||
6597 | tcp->window = lro->window; | ||
6598 | |||
6599 | /* Update tsecr field if this session has timestamps enabled */ | ||
6600 | if (lro->saw_ts) { | ||
6601 | u32 *ptr = (u32 *)(tcp + 1); | ||
6602 | *(ptr+2) = lro->cur_tsecr; | ||
6603 | } | ||
6604 | |||
6605 | /* Update counters required for calculation of | ||
6606 | * average no. of packets aggregated. | ||
6607 | */ | ||
6608 | statinfo->sw_stat.sum_avg_pkts_aggregated += lro->sg_num; | ||
6609 | statinfo->sw_stat.num_aggregations++; | ||
6610 | } | ||
6611 | |||
6612 | static void aggregate_new_rx(lro_t *lro, struct iphdr *ip, | ||
6613 | struct tcphdr *tcp, u32 l4_pyld) | ||
6614 | { | ||
6615 | DBG_PRINT(INFO_DBG,"%s: Been here...\n", __FUNCTION__); | ||
6616 | lro->total_len += l4_pyld; | ||
6617 | lro->frags_len += l4_pyld; | ||
6618 | lro->tcp_next_seq += l4_pyld; | ||
6619 | lro->sg_num++; | ||
6620 | |||
6621 | /* Update ack seq no. and window ad(from this pkt) in LRO object */ | ||
6622 | lro->tcp_ack = tcp->ack_seq; | ||
6623 | lro->window = tcp->window; | ||
6624 | |||
6625 | if (lro->saw_ts) { | ||
6626 | u32 *ptr; | ||
6627 | /* Update tsecr and tsval from this packet */ | ||
6628 | ptr = (u32 *) (tcp + 1); | ||
6629 | lro->cur_tsval = *(ptr + 1); | ||
6630 | lro->cur_tsecr = *(ptr + 2); | ||
6631 | } | ||
6632 | } | ||
6633 | |||
6634 | static int verify_l3_l4_lro_capable(lro_t *l_lro, struct iphdr *ip, | ||
6635 | struct tcphdr *tcp, u32 tcp_pyld_len) | ||
6636 | { | ||
6637 | u8 *ptr; | ||
6638 | |||
6639 | DBG_PRINT(INFO_DBG,"%s: Been here...\n", __FUNCTION__); | ||
6640 | |||
6641 | if (!tcp_pyld_len) { | ||
6642 | /* Runt frame or a pure ack */ | ||
6643 | return -1; | ||
6644 | } | ||
6645 | |||
6646 | if (ip->ihl != 5) /* IP has options */ | ||
6647 | return -1; | ||
6648 | |||
6649 | if (tcp->urg || tcp->psh || tcp->rst || tcp->syn || tcp->fin || | ||
6650 | !tcp->ack) { | ||
6651 | /* | ||
6652 | * Currently recognize only the ack control word and | ||
6653 | * any other control field being set would result in | ||
6654 | * flushing the LRO session | ||
6655 | */ | ||
6656 | return -1; | ||
6657 | } | ||
6658 | |||
6659 | /* | ||
6660 | * Allow only one TCP timestamp option. Don't aggregate if | ||
6661 | * any other options are detected. | ||
6662 | */ | ||
6663 | if (tcp->doff != 5 && tcp->doff != 8) | ||
6664 | return -1; | ||
6665 | |||
6666 | if (tcp->doff == 8) { | ||
6667 | ptr = (u8 *)(tcp + 1); | ||
6668 | while (*ptr == TCPOPT_NOP) | ||
6669 | ptr++; | ||
6670 | if (*ptr != TCPOPT_TIMESTAMP || *(ptr+1) != TCPOLEN_TIMESTAMP) | ||
6671 | return -1; | ||
6672 | |||
6673 | /* Ensure timestamp value increases monotonically */ | ||
6674 | if (l_lro) | ||
6675 | if (l_lro->cur_tsval > *((u32 *)(ptr+2))) | ||
6676 | return -1; | ||
6677 | |||
6678 | /* timestamp echo reply should be non-zero */ | ||
6679 | if (*((u32 *)(ptr+6)) == 0) | ||
6680 | return -1; | ||
6681 | } | ||
6682 | |||
6683 | return 0; | ||
6684 | } | ||
6685 | |||
6686 | static int | ||
6687 | s2io_club_tcp_session(u8 *buffer, u8 **tcp, u32 *tcp_len, lro_t **lro, | ||
6688 | RxD_t *rxdp, nic_t *sp) | ||
6689 | { | ||
6690 | struct iphdr *ip; | ||
6691 | struct tcphdr *tcph; | ||
6692 | int ret = 0, i; | ||
6693 | |||
6694 | if (!(ret = check_L2_lro_capable(buffer, &ip, (struct tcphdr **)tcp, | ||
6695 | rxdp))) { | ||
6696 | DBG_PRINT(INFO_DBG,"IP Saddr: %x Daddr: %x\n", | ||
6697 | ip->saddr, ip->daddr); | ||
6698 | } else { | ||
6699 | return ret; | ||
6700 | } | ||
6701 | |||
6702 | tcph = (struct tcphdr *)*tcp; | ||
6703 | *tcp_len = get_l4_pyld_length(ip, tcph); | ||
6704 | for (i=0; i<MAX_LRO_SESSIONS; i++) { | ||
6705 | lro_t *l_lro = &sp->lro0_n[i]; | ||
6706 | if (l_lro->in_use) { | ||
6707 | if (check_for_socket_match(l_lro, ip, tcph)) | ||
6708 | continue; | ||
6709 | /* Sock pair matched */ | ||
6710 | *lro = l_lro; | ||
6711 | |||
6712 | if ((*lro)->tcp_next_seq != ntohl(tcph->seq)) { | ||
6713 | DBG_PRINT(INFO_DBG, "%s:Out of order. expected " | ||
6714 | "0x%x, actual 0x%x\n", __FUNCTION__, | ||
6715 | (*lro)->tcp_next_seq, | ||
6716 | ntohl(tcph->seq)); | ||
6717 | |||
6718 | sp->mac_control.stats_info-> | ||
6719 | sw_stat.outof_sequence_pkts++; | ||
6720 | ret = 2; | ||
6721 | break; | ||
6722 | } | ||
6723 | |||
6724 | if (!verify_l3_l4_lro_capable(l_lro, ip, tcph,*tcp_len)) | ||
6725 | ret = 1; /* Aggregate */ | ||
6726 | else | ||
6727 | ret = 2; /* Flush both */ | ||
6728 | break; | ||
6729 | } | ||
6730 | } | ||
6731 | |||
6732 | if (ret == 0) { | ||
6733 | /* Before searching for available LRO objects, | ||
6734 | * check if the pkt is L3/L4 aggregatable. If not | ||
6735 | * don't create new LRO session. Just send this | ||
6736 | * packet up. | ||
6737 | */ | ||
6738 | if (verify_l3_l4_lro_capable(NULL, ip, tcph, *tcp_len)) { | ||
6739 | return 5; | ||
6740 | } | ||
6741 | |||
6742 | for (i=0; i<MAX_LRO_SESSIONS; i++) { | ||
6743 | lro_t *l_lro = &sp->lro0_n[i]; | ||
6744 | if (!(l_lro->in_use)) { | ||
6745 | *lro = l_lro; | ||
6746 | ret = 3; /* Begin anew */ | ||
6747 | break; | ||
6748 | } | ||
6749 | } | ||
6750 | } | ||
6751 | |||
6752 | if (ret == 0) { /* sessions exceeded */ | ||
6753 | DBG_PRINT(INFO_DBG,"%s:All LRO sessions already in use\n", | ||
6754 | __FUNCTION__); | ||
6755 | *lro = NULL; | ||
6756 | return ret; | ||
6757 | } | ||
6758 | |||
6759 | switch (ret) { | ||
6760 | case 3: | ||
6761 | initiate_new_session(*lro, buffer, ip, tcph, *tcp_len); | ||
6762 | break; | ||
6763 | case 2: | ||
6764 | update_L3L4_header(sp, *lro); | ||
6765 | break; | ||
6766 | case 1: | ||
6767 | aggregate_new_rx(*lro, ip, tcph, *tcp_len); | ||
6768 | if ((*lro)->sg_num == sp->lro_max_aggr_per_sess) { | ||
6769 | update_L3L4_header(sp, *lro); | ||
6770 | ret = 4; /* Flush the LRO */ | ||
6771 | } | ||
6772 | break; | ||
6773 | default: | ||
6774 | DBG_PRINT(ERR_DBG,"%s:Dont know, can't say!!\n", | ||
6775 | __FUNCTION__); | ||
6776 | break; | ||
6777 | } | ||
6778 | |||
6779 | return ret; | ||
6780 | } | ||
6781 | |||
6782 | static void clear_lro_session(lro_t *lro) | ||
6783 | { | ||
6784 | static u16 lro_struct_size = sizeof(lro_t); | ||
6785 | |||
6786 | memset(lro, 0, lro_struct_size); | ||
6787 | } | ||
6788 | |||
6789 | static void queue_rx_frame(struct sk_buff *skb) | ||
6790 | { | ||
6791 | struct net_device *dev = skb->dev; | ||
6792 | |||
6793 | skb->protocol = eth_type_trans(skb, dev); | ||
6794 | #ifdef CONFIG_S2IO_NAPI | ||
6795 | netif_receive_skb(skb); | ||
6796 | #else | ||
6797 | netif_rx(skb); | ||
6798 | #endif | ||
6799 | } | ||
6800 | |||
6801 | static void lro_append_pkt(nic_t *sp, lro_t *lro, struct sk_buff *skb, | ||
6802 | u32 tcp_len) | ||
6803 | { | ||
6804 | struct sk_buff *tmp, *first = lro->parent; | ||
6805 | |||
6806 | first->len += tcp_len; | ||
6807 | first->data_len = lro->frags_len; | ||
6808 | skb_pull(skb, (skb->len - tcp_len)); | ||
6809 | if ((tmp = skb_shinfo(first)->frag_list)) { | ||
6810 | while (tmp->next) | ||
6811 | tmp = tmp->next; | ||
6812 | tmp->next = skb; | ||
6813 | } | ||
6814 | else | ||
6815 | skb_shinfo(first)->frag_list = skb; | ||
6816 | sp->mac_control.stats_info->sw_stat.clubbed_frms_cnt++; | ||
6817 | return; | ||
6818 | } | ||