Loading drivers/net/s2io.c +527 −62 Original line number Diff line number Diff line Loading @@ -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"; Loading Loading @@ -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 Loading Loading @@ -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. Loading Loading @@ -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. Loading Loading @@ -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", Loading Loading @@ -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); } Loading Loading @@ -3668,11 +3710,13 @@ 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++) { 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, "%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", Loading @@ -3687,6 +3731,13 @@ s2io_msi_handle(int irq, void *dev_id, struct pt_regs *regs) 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; } } atomic_dec(&sp->isr_cnt); return IRQ_HANDLED; Loading @@ -3697,12 +3748,14 @@ 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; if (!sp->lro) { rxb_size = atomic_read(&sp->rx_bufs_left[rng_n]); level = rx_buffer_level(sp, rxb_size, rng_n); Loading @@ -3720,6 +3773,12 @@ s2io_msix_ring_handle(int irq, void *dev_id, struct pt_regs *regs) } 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; Loading Loading @@ -3875,12 +3934,14 @@ 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++) { 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, "%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", Loading @@ -3895,6 +3956,13 @@ static irqreturn_t s2io_isr(int irq, void *dev_id, struct pt_regs *regs) 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 atomic_dec(&sp->isr_cnt); Loading Loading @@ -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++] = Loading Loading @@ -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) Loading Loading @@ -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); Loading Loading @@ -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) { Loading Loading @@ -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); Loading @@ -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 Loading @@ -5677,6 +5814,7 @@ static int rx_osm_handler(ring_info_t *ring_data, RxD_t * rxdp) skb->ip_summed = CHECKSUM_NONE; } if (!sp->lro) { skb->protocol = eth_type_trans(skb, dev); #ifdef CONFIG_S2IO_NAPI if (sp->vlgrp && RXD_GET_VLAN_TAG(rxdp->Control_2)) { Loading @@ -5695,7 +5833,12 @@ static int rx_osm_handler(ring_info_t *ring_data, RxD_t * rxdp) 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; } Loading Loading @@ -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 . Loading Loading @@ -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); Loading Loading @@ -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) Loading Loading @@ -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; } drivers/net/s2io.h +38 −0 Original line number Diff line number Diff line Loading @@ -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 */ Loading Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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
drivers/net/s2io.c +527 −62 Original line number Diff line number Diff line Loading @@ -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"; Loading Loading @@ -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 Loading Loading @@ -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. Loading Loading @@ -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. Loading Loading @@ -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", Loading Loading @@ -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); } Loading Loading @@ -3668,11 +3710,13 @@ 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++) { 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, "%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", Loading @@ -3687,6 +3731,13 @@ s2io_msi_handle(int irq, void *dev_id, struct pt_regs *regs) 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; } } atomic_dec(&sp->isr_cnt); return IRQ_HANDLED; Loading @@ -3697,12 +3748,14 @@ 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; if (!sp->lro) { rxb_size = atomic_read(&sp->rx_bufs_left[rng_n]); level = rx_buffer_level(sp, rxb_size, rng_n); Loading @@ -3720,6 +3773,12 @@ s2io_msix_ring_handle(int irq, void *dev_id, struct pt_regs *regs) } 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; Loading Loading @@ -3875,12 +3934,14 @@ 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++) { 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, "%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", Loading @@ -3895,6 +3956,13 @@ static irqreturn_t s2io_isr(int irq, void *dev_id, struct pt_regs *regs) 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 atomic_dec(&sp->isr_cnt); Loading Loading @@ -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++] = Loading Loading @@ -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) Loading Loading @@ -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); Loading Loading @@ -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) { Loading Loading @@ -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); Loading @@ -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 Loading @@ -5677,6 +5814,7 @@ static int rx_osm_handler(ring_info_t *ring_data, RxD_t * rxdp) skb->ip_summed = CHECKSUM_NONE; } if (!sp->lro) { skb->protocol = eth_type_trans(skb, dev); #ifdef CONFIG_S2IO_NAPI if (sp->vlgrp && RXD_GET_VLAN_TAG(rxdp->Control_2)) { Loading @@ -5695,7 +5833,12 @@ static int rx_osm_handler(ring_info_t *ring_data, RxD_t * rxdp) 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; } Loading Loading @@ -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 . Loading Loading @@ -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); Loading Loading @@ -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) Loading Loading @@ -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; }
drivers/net/s2io.h +38 −0 Original line number Diff line number Diff line Loading @@ -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 */ Loading Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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 */