-
-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 we see CE codepoint in IP header, packet is not mergeable */
- if (INET_ECN_is_ce(ipv4_get_dsfield(ip)))
- return -1;
-
- /* If we see ECE or CWR flags in TCP header, packet is not mergeable */
- if (tcp->urg || tcp->psh || tcp->rst || tcp->syn || tcp->fin ||
- tcp->ece || tcp->cwr || !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 *first = lro->parent;
-
- first->len += tcp_len;
- first->data_len = lro->frags_len;
- skb_pull(skb, (skb->len - tcp_len));
- if (skb_shinfo(first)->frag_list)
- lro->last_frag->next = skb;
- else
- skb_shinfo(first)->frag_list = skb;
- lro->last_frag = skb;
- sp->mac_control.stats_info->sw_stat.clubbed_frms_cnt++;
- return;
-}