xref: /onnv-gate/usr/src/uts/common/io/myri10ge/drv/myri10ge_lro.c (revision 11878:ac93462db6d7)
110253Sxiuyan.wang@Sun.COM /*
210253Sxiuyan.wang@Sun.COM  * CDDL HEADER START
310253Sxiuyan.wang@Sun.COM  *
410253Sxiuyan.wang@Sun.COM  * The contents of this file are subject to the terms of the
510253Sxiuyan.wang@Sun.COM  * Common Development and Distribution License (the "License").
610253Sxiuyan.wang@Sun.COM  * You may not use this file except in compliance with the License.
710253Sxiuyan.wang@Sun.COM  *
810253Sxiuyan.wang@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910253Sxiuyan.wang@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1010253Sxiuyan.wang@Sun.COM  * See the License for the specific language governing permissions
1110253Sxiuyan.wang@Sun.COM  * and limitations under the License.
1210253Sxiuyan.wang@Sun.COM  *
1310253Sxiuyan.wang@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1410253Sxiuyan.wang@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510253Sxiuyan.wang@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1610253Sxiuyan.wang@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1710253Sxiuyan.wang@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1810253Sxiuyan.wang@Sun.COM  *
1910253Sxiuyan.wang@Sun.COM  * CDDL HEADER END
2010253Sxiuyan.wang@Sun.COM  */
2110253Sxiuyan.wang@Sun.COM 
2210253Sxiuyan.wang@Sun.COM /*
2310253Sxiuyan.wang@Sun.COM  * Copyright 2007-2009 Myricom, Inc.  All rights reserved.
2410253Sxiuyan.wang@Sun.COM  * Use is subject to license terms.
2510253Sxiuyan.wang@Sun.COM  */
2610253Sxiuyan.wang@Sun.COM 
2710253Sxiuyan.wang@Sun.COM #ifndef lint
2810253Sxiuyan.wang@Sun.COM static const char __idstring[] =
2910253Sxiuyan.wang@Sun.COM 	"@(#)$Id: myri10ge_lro.c,v 1.7 2009-06-29 13:47:22 gallatin Exp $";
3010253Sxiuyan.wang@Sun.COM #endif
3110253Sxiuyan.wang@Sun.COM 
3210253Sxiuyan.wang@Sun.COM #include "myri10ge_var.h"
3310253Sxiuyan.wang@Sun.COM 
3410253Sxiuyan.wang@Sun.COM #define	IP_OFFMASK 0x1fff
3510253Sxiuyan.wang@Sun.COM #define	TCPOPT_TIMESTAMP 8
3610253Sxiuyan.wang@Sun.COM #define	TCPOLEN_TIMESTAMP 10
3710253Sxiuyan.wang@Sun.COM #define	TCPOLEN_TSTAMP_APPA 12
3810253Sxiuyan.wang@Sun.COM 
3910253Sxiuyan.wang@Sun.COM 
4010253Sxiuyan.wang@Sun.COM /*
4110253Sxiuyan.wang@Sun.COM  * Assume len is a multiple of 4. Note that "raw" must be
4210253Sxiuyan.wang@Sun.COM  * suitably aligned. In practice, it will always enter algned on
4310253Sxiuyan.wang@Sun.COM  * at least a 4 bytes bounday, due to the alignment of our rx buffers.
4410253Sxiuyan.wang@Sun.COM  */
4510253Sxiuyan.wang@Sun.COM uint16_t
myri10ge_csum_generic(uint16_t * raw,int len)4610253Sxiuyan.wang@Sun.COM myri10ge_csum_generic(uint16_t *raw, int len)
4710253Sxiuyan.wang@Sun.COM {
4810253Sxiuyan.wang@Sun.COM 	uint32_t csum;
4910253Sxiuyan.wang@Sun.COM 	csum = 0;
5010253Sxiuyan.wang@Sun.COM 	while (len > 0) {
5110253Sxiuyan.wang@Sun.COM 		csum += *raw;
5210253Sxiuyan.wang@Sun.COM 		raw++;
5310253Sxiuyan.wang@Sun.COM 		csum += *raw;
5410253Sxiuyan.wang@Sun.COM 		raw++;
5510253Sxiuyan.wang@Sun.COM 		len -= 4;
5610253Sxiuyan.wang@Sun.COM 	}
5710253Sxiuyan.wang@Sun.COM 	csum = (csum >> 16) + (csum & 0xffff);
5810253Sxiuyan.wang@Sun.COM 	csum = (csum >> 16) + (csum & 0xffff);
5910253Sxiuyan.wang@Sun.COM 	return ((uint16_t)csum);
6010253Sxiuyan.wang@Sun.COM }
6110253Sxiuyan.wang@Sun.COM 
6210253Sxiuyan.wang@Sun.COM static uint16_t
myri10ge_in_pseudo(unsigned int a,unsigned int b,unsigned int c)6310253Sxiuyan.wang@Sun.COM myri10ge_in_pseudo(unsigned int a, unsigned int b,
6410253Sxiuyan.wang@Sun.COM     unsigned int c)
6510253Sxiuyan.wang@Sun.COM {
6610253Sxiuyan.wang@Sun.COM 	uint64_t csum;
6710253Sxiuyan.wang@Sun.COM 
6810253Sxiuyan.wang@Sun.COM 	csum = (uint64_t)a + b + c;
6910253Sxiuyan.wang@Sun.COM 	csum = (csum >> 16) + (csum & 0xffff);
7010253Sxiuyan.wang@Sun.COM 	csum = (csum >> 16) + (csum & 0xffff);
7110253Sxiuyan.wang@Sun.COM 	return ((uint16_t)csum);
7210253Sxiuyan.wang@Sun.COM }
7310253Sxiuyan.wang@Sun.COM 
7410253Sxiuyan.wang@Sun.COM void
myri10ge_lro_flush(struct myri10ge_slice_state * ss,struct lro_entry * lro,struct myri10ge_mblk_list * mbl)7510253Sxiuyan.wang@Sun.COM myri10ge_lro_flush(struct myri10ge_slice_state *ss, struct lro_entry *lro,
7610253Sxiuyan.wang@Sun.COM 	struct myri10ge_mblk_list *mbl)
7710253Sxiuyan.wang@Sun.COM {
7810253Sxiuyan.wang@Sun.COM 	struct ip *ip;
7910253Sxiuyan.wang@Sun.COM 	struct tcphdr *tcp;
8010253Sxiuyan.wang@Sun.COM 	uint32_t *ts_ptr;
8110253Sxiuyan.wang@Sun.COM 	uint32_t tcplen, tcp_csum;
8210253Sxiuyan.wang@Sun.COM 
8310253Sxiuyan.wang@Sun.COM 	if (lro->append_cnt) {
8410253Sxiuyan.wang@Sun.COM 		/*
8510253Sxiuyan.wang@Sun.COM 		 * incorporate the new len into the ip header and
8610253Sxiuyan.wang@Sun.COM 		 * re-calculate the checksum
8710253Sxiuyan.wang@Sun.COM 		 */
8810253Sxiuyan.wang@Sun.COM 		ip = lro->ip;
8910253Sxiuyan.wang@Sun.COM 		ip->ip_len = htons(lro->len - ETHERNET_HEADER_SIZE);
9010253Sxiuyan.wang@Sun.COM 		ip->ip_sum = 0;
9110253Sxiuyan.wang@Sun.COM 		ip->ip_sum = 0xffff ^
9210253Sxiuyan.wang@Sun.COM 		    myri10ge_csum_generic((uint16_t *)ip, sizeof (*ip));
9310253Sxiuyan.wang@Sun.COM 		/* incorporate the latest ack into the tcp header */
9410253Sxiuyan.wang@Sun.COM 		tcp = (struct tcphdr *)(ip + 1);
9510253Sxiuyan.wang@Sun.COM 		tcp->th_ack = lro->ack_seq;
9610253Sxiuyan.wang@Sun.COM 		tcp->th_win = lro->window;
9710253Sxiuyan.wang@Sun.COM 		tcp->th_flags = lro->flags;
9810253Sxiuyan.wang@Sun.COM 		/* incorporate latest timestamp into the tcp header */
9910253Sxiuyan.wang@Sun.COM 		if (lro->timestamp) {
10010253Sxiuyan.wang@Sun.COM 			ts_ptr = (uint32_t *)(tcp + 1);
10110253Sxiuyan.wang@Sun.COM 			ts_ptr[1] = htonl(lro->tsval);
10210253Sxiuyan.wang@Sun.COM 			ts_ptr[2] = lro->tsecr;
10310253Sxiuyan.wang@Sun.COM 		}
10410253Sxiuyan.wang@Sun.COM 		/*
10510253Sxiuyan.wang@Sun.COM 		 * update checksum in tcp header by re-calculating the
10610253Sxiuyan.wang@Sun.COM 		 * tcp pseudoheader checksum, and adding it to the checksum
10710253Sxiuyan.wang@Sun.COM 		 * of the tcp payload data
10810253Sxiuyan.wang@Sun.COM 		 */
10910253Sxiuyan.wang@Sun.COM 		tcp->th_sum = 0;
11010253Sxiuyan.wang@Sun.COM 		tcplen = lro->len - sizeof (*ip) - ETHERNET_HEADER_SIZE;
11110253Sxiuyan.wang@Sun.COM 		tcp_csum = lro->data_csum;
11210253Sxiuyan.wang@Sun.COM 		tcp_csum += myri10ge_in_pseudo(ip->ip_src.s_addr,
11310253Sxiuyan.wang@Sun.COM 		    ip->ip_dst.s_addr, htons(tcplen + IPPROTO_TCP));
11410253Sxiuyan.wang@Sun.COM 		tcp_csum += myri10ge_csum_generic((uint16_t *)tcp,
11510253Sxiuyan.wang@Sun.COM 		    tcp->th_off << 2);
11610253Sxiuyan.wang@Sun.COM 		tcp_csum = (tcp_csum & 0xffff) + (tcp_csum >> 16);
11710253Sxiuyan.wang@Sun.COM 		tcp_csum = (tcp_csum & 0xffff) + (tcp_csum >> 16);
11810253Sxiuyan.wang@Sun.COM 		tcp->th_sum = 0xffff ^ tcp_csum;
11910253Sxiuyan.wang@Sun.COM 	}
12010253Sxiuyan.wang@Sun.COM 
121*11878SVenu.Iyer@Sun.COM 	mac_hcksum_set(lro->m_head, 0, 0, 0,
122*11878SVenu.Iyer@Sun.COM 	    0, HCK_IPV4_HDRCKSUM_OK | HCK_FULLCKSUM_OK);
12310253Sxiuyan.wang@Sun.COM 
12410253Sxiuyan.wang@Sun.COM 	mbl->cnt += lro->append_cnt;
12510253Sxiuyan.wang@Sun.COM 	myri10ge_mbl_append(ss, mbl, lro->m_head);
12610253Sxiuyan.wang@Sun.COM 	MYRI10GE_SLICE_STAT_INC(lro_flushed);
12710253Sxiuyan.wang@Sun.COM 	MYRI10GE_SLICE_STAT_ADD(lro_queued, lro->append_cnt + 1);
12810253Sxiuyan.wang@Sun.COM 	lro->m_head = NULL;
12910253Sxiuyan.wang@Sun.COM 	lro->timestamp = 0;
13010253Sxiuyan.wang@Sun.COM 	lro->append_cnt = 0;
13110253Sxiuyan.wang@Sun.COM 	lro->next = ss->lro_free;
13210253Sxiuyan.wang@Sun.COM 	ss->lro_free = lro;
13310253Sxiuyan.wang@Sun.COM }
13410253Sxiuyan.wang@Sun.COM 
13510253Sxiuyan.wang@Sun.COM int
myri10ge_lro_rx(struct myri10ge_slice_state * ss,mblk_t * m_head,uint32_t csum,struct myri10ge_mblk_list * mbl)13610253Sxiuyan.wang@Sun.COM myri10ge_lro_rx(struct myri10ge_slice_state *ss, mblk_t *m_head,
13710253Sxiuyan.wang@Sun.COM 		uint32_t csum, struct myri10ge_mblk_list *mbl)
13810253Sxiuyan.wang@Sun.COM {
13910253Sxiuyan.wang@Sun.COM 	struct ether_header *eh;
14010253Sxiuyan.wang@Sun.COM 	struct ip *ip;
14110253Sxiuyan.wang@Sun.COM 	struct tcphdr *tcp;
14210253Sxiuyan.wang@Sun.COM 	uint32_t *ts_ptr;
14310253Sxiuyan.wang@Sun.COM 	struct lro_entry *lro, *curr;
14410253Sxiuyan.wang@Sun.COM 	int hlen, ip_len, tcp_hdr_len, tcp_data_len;
14510253Sxiuyan.wang@Sun.COM 	int opt_bytes, trim;
14610253Sxiuyan.wang@Sun.COM 	int tot_len = MBLKL(m_head);
14710253Sxiuyan.wang@Sun.COM 	uint32_t seq, tmp_csum;
14810253Sxiuyan.wang@Sun.COM 
14910253Sxiuyan.wang@Sun.COM 	eh = (struct ether_header *)(void *)m_head->b_rptr;
15010253Sxiuyan.wang@Sun.COM 	if (eh->ether_type != htons(ETHERTYPE_IP))
15110253Sxiuyan.wang@Sun.COM 		return (EINVAL);
15210253Sxiuyan.wang@Sun.COM 	ip = (struct ip *)(void *)(eh + 1);
15310253Sxiuyan.wang@Sun.COM 	if (ip->ip_p != IPPROTO_TCP)
15410253Sxiuyan.wang@Sun.COM 		return (EINVAL);
15510253Sxiuyan.wang@Sun.COM 
15610253Sxiuyan.wang@Sun.COM 	/* ensure there are no options */
15710253Sxiuyan.wang@Sun.COM 	if ((ip->ip_hl << 2) != sizeof (*ip))
15810253Sxiuyan.wang@Sun.COM 		return (EINVAL);
15910253Sxiuyan.wang@Sun.COM 
16010253Sxiuyan.wang@Sun.COM 	/* .. and the packet is not fragmented */
16110253Sxiuyan.wang@Sun.COM 	if (ip->ip_off & htons(IP_MF|IP_OFFMASK))
16210253Sxiuyan.wang@Sun.COM 		return (EINVAL);
16310253Sxiuyan.wang@Sun.COM 
16410253Sxiuyan.wang@Sun.COM 	/* verify that the IP header checksum is correct */
16510253Sxiuyan.wang@Sun.COM 	tmp_csum = myri10ge_csum_generic((uint16_t *)ip, sizeof (*ip));
16610253Sxiuyan.wang@Sun.COM 	if (unlikely((tmp_csum ^ 0xffff) != 0)) {
16710253Sxiuyan.wang@Sun.COM 		MYRI10GE_SLICE_STAT_INC(lro_bad_csum);
16810253Sxiuyan.wang@Sun.COM 		return (EINVAL);
16910253Sxiuyan.wang@Sun.COM 	}
17010253Sxiuyan.wang@Sun.COM 
17110253Sxiuyan.wang@Sun.COM 	/* find the TCP header */
17210253Sxiuyan.wang@Sun.COM 	tcp = (struct tcphdr *)(ip + 1);
17310253Sxiuyan.wang@Sun.COM 
17410253Sxiuyan.wang@Sun.COM 	/* ensure no bits set besides ack or psh */
17510253Sxiuyan.wang@Sun.COM 	if ((tcp->th_flags & ~(TH_ACK | TH_PUSH)) != 0)
17610253Sxiuyan.wang@Sun.COM 		return (EINVAL);
17710253Sxiuyan.wang@Sun.COM 
17810253Sxiuyan.wang@Sun.COM 	/*
17910253Sxiuyan.wang@Sun.COM 	 * check for timestamps. Since the only option we handle are
18010253Sxiuyan.wang@Sun.COM 	 * timestamps, we only have to handle the simple case of
18110253Sxiuyan.wang@Sun.COM 	 * aligned timestamps
18210253Sxiuyan.wang@Sun.COM 	 */
18310253Sxiuyan.wang@Sun.COM 
18410253Sxiuyan.wang@Sun.COM 	opt_bytes = (tcp->th_off << 2) - sizeof (*tcp);
18510253Sxiuyan.wang@Sun.COM 	tcp_hdr_len =  sizeof (*tcp) + opt_bytes;
18610253Sxiuyan.wang@Sun.COM 	ts_ptr = (uint32_t *)(tcp + 1);
18710253Sxiuyan.wang@Sun.COM 	if (opt_bytes != 0) {
18810253Sxiuyan.wang@Sun.COM 		if (unlikely(opt_bytes != TCPOLEN_TSTAMP_APPA) ||
18910253Sxiuyan.wang@Sun.COM 		    (*ts_ptr !=  ntohl(TCPOPT_NOP<<24|TCPOPT_NOP<<16|
19010253Sxiuyan.wang@Sun.COM 		    TCPOPT_TIMESTAMP<<8|TCPOLEN_TIMESTAMP)))
19110253Sxiuyan.wang@Sun.COM 			return (EINVAL);
19210253Sxiuyan.wang@Sun.COM 	}
19310253Sxiuyan.wang@Sun.COM 
19410253Sxiuyan.wang@Sun.COM 	ip_len = ntohs(ip->ip_len);
19510253Sxiuyan.wang@Sun.COM 	tcp_data_len = ip_len - (tcp->th_off << 2) - sizeof (*ip);
19610253Sxiuyan.wang@Sun.COM 
19710253Sxiuyan.wang@Sun.COM 	/*
19810253Sxiuyan.wang@Sun.COM 	 * If frame is padded beyond the end of the IP packet,
19910253Sxiuyan.wang@Sun.COM 	 * then we must trim the extra bytes off the end.
20010253Sxiuyan.wang@Sun.COM 	 */
20110253Sxiuyan.wang@Sun.COM 	trim = tot_len - (ip_len + ETHERNET_HEADER_SIZE);
20210253Sxiuyan.wang@Sun.COM 	if (trim != 0) {
20310253Sxiuyan.wang@Sun.COM 		if (trim < 0) {
20410253Sxiuyan.wang@Sun.COM 			/* truncated packet */
20510253Sxiuyan.wang@Sun.COM 			return (EINVAL);
20610253Sxiuyan.wang@Sun.COM 		}
20710253Sxiuyan.wang@Sun.COM 		m_head->b_wptr -= trim;
20810253Sxiuyan.wang@Sun.COM 		tot_len -= trim;
20910253Sxiuyan.wang@Sun.COM 	}
21010253Sxiuyan.wang@Sun.COM 
21110253Sxiuyan.wang@Sun.COM 	/* Verify TCP checksum */
21210253Sxiuyan.wang@Sun.COM 	csum = ntohs((uint16_t)csum);
21310253Sxiuyan.wang@Sun.COM 	tmp_csum = csum + myri10ge_in_pseudo(ip->ip_src.s_addr,
21410253Sxiuyan.wang@Sun.COM 	    ip->ip_dst.s_addr, htons(tcp_hdr_len + tcp_data_len + IPPROTO_TCP));
21510253Sxiuyan.wang@Sun.COM 	tmp_csum = (tmp_csum & 0xffff) + (tmp_csum >> 16);
21610253Sxiuyan.wang@Sun.COM 	tmp_csum = (tmp_csum & 0xffff) + (tmp_csum >> 16);
21710253Sxiuyan.wang@Sun.COM 	if (tmp_csum != 0xffff) {
21810253Sxiuyan.wang@Sun.COM 		MYRI10GE_SLICE_STAT_INC(lro_bad_csum);
21910253Sxiuyan.wang@Sun.COM 		return (EINVAL);
22010253Sxiuyan.wang@Sun.COM 	}
22110253Sxiuyan.wang@Sun.COM 
22210253Sxiuyan.wang@Sun.COM 	hlen = ip_len + ETHERNET_HEADER_SIZE - tcp_data_len;
22310253Sxiuyan.wang@Sun.COM 	seq = ntohl(tcp->th_seq);
22410253Sxiuyan.wang@Sun.COM 
22510253Sxiuyan.wang@Sun.COM 	for (lro = ss->lro_active; lro != NULL; lro = lro->next) {
22610253Sxiuyan.wang@Sun.COM 		if (lro->source_port == tcp->th_sport &&
22710253Sxiuyan.wang@Sun.COM 		    lro->dest_port == tcp->th_dport &&
22810253Sxiuyan.wang@Sun.COM 		    lro->source_ip == ip->ip_src.s_addr &&
22910253Sxiuyan.wang@Sun.COM 		    lro->dest_ip == ip->ip_dst.s_addr) {
23010253Sxiuyan.wang@Sun.COM 			/* Try to append it */
23110253Sxiuyan.wang@Sun.COM 
23210253Sxiuyan.wang@Sun.COM 			if (unlikely(seq != lro->next_seq)) {
23310253Sxiuyan.wang@Sun.COM 				/* out of order packet */
23410253Sxiuyan.wang@Sun.COM 				if (ss->lro_active == lro) {
23510253Sxiuyan.wang@Sun.COM 					ss->lro_active = lro->next;
23610253Sxiuyan.wang@Sun.COM 				} else {
23710253Sxiuyan.wang@Sun.COM 					curr = ss->lro_active;
23810253Sxiuyan.wang@Sun.COM 					while (curr->next != lro)
23910253Sxiuyan.wang@Sun.COM 						curr = curr->next;
24010253Sxiuyan.wang@Sun.COM 					curr->next = lro->next;
24110253Sxiuyan.wang@Sun.COM 				}
24210253Sxiuyan.wang@Sun.COM 				myri10ge_lro_flush(ss, lro, mbl);
24310253Sxiuyan.wang@Sun.COM 				return (EINVAL);
24410253Sxiuyan.wang@Sun.COM 			}
24510253Sxiuyan.wang@Sun.COM 
24610253Sxiuyan.wang@Sun.COM 			if (opt_bytes) {
24710253Sxiuyan.wang@Sun.COM 				uint32_t tsval = ntohl(*(ts_ptr + 1));
24810253Sxiuyan.wang@Sun.COM 				/* make sure timestamp values are increasing */
24910253Sxiuyan.wang@Sun.COM 				if (unlikely(lro->tsval > tsval ||
25010253Sxiuyan.wang@Sun.COM 				    *(ts_ptr + 2) == 0)) {
25110253Sxiuyan.wang@Sun.COM 					return (-8);
25210253Sxiuyan.wang@Sun.COM 				}
25310253Sxiuyan.wang@Sun.COM 				lro->tsval = tsval;
25410253Sxiuyan.wang@Sun.COM 				lro->tsecr = *(ts_ptr + 2);
25510253Sxiuyan.wang@Sun.COM 			}
25610253Sxiuyan.wang@Sun.COM 
25710253Sxiuyan.wang@Sun.COM 			lro->next_seq += tcp_data_len;
25810253Sxiuyan.wang@Sun.COM 			lro->ack_seq = tcp->th_ack;
25910253Sxiuyan.wang@Sun.COM 			lro->window = tcp->th_win;
26010253Sxiuyan.wang@Sun.COM 			lro->flags |= tcp->th_flags;
26110253Sxiuyan.wang@Sun.COM 			lro->append_cnt++;
26210253Sxiuyan.wang@Sun.COM 			if (tcp_data_len == 0) {
26310253Sxiuyan.wang@Sun.COM 				freeb(m_head);
26410253Sxiuyan.wang@Sun.COM 				return (0);
26510253Sxiuyan.wang@Sun.COM 			}
26610253Sxiuyan.wang@Sun.COM 			/*
26710253Sxiuyan.wang@Sun.COM 			 * subtract off the checksum of the tcp header
26810253Sxiuyan.wang@Sun.COM 			 * from the hardware checksum, and add it to
26910253Sxiuyan.wang@Sun.COM 			 * the stored tcp data checksum.  Byteswap
27010253Sxiuyan.wang@Sun.COM 			 * the checksum if the total length so far is
27110253Sxiuyan.wang@Sun.COM 			 * odd
27210253Sxiuyan.wang@Sun.COM 			 */
27310253Sxiuyan.wang@Sun.COM 			tmp_csum = myri10ge_csum_generic((uint16_t *)tcp,
27410253Sxiuyan.wang@Sun.COM 			    tcp_hdr_len);
27510253Sxiuyan.wang@Sun.COM 			csum = csum + (tmp_csum ^ 0xffff);
27610253Sxiuyan.wang@Sun.COM 			csum = (csum & 0xffff) + (csum >> 16);
27710253Sxiuyan.wang@Sun.COM 			csum = (csum & 0xffff) + (csum >> 16);
27810253Sxiuyan.wang@Sun.COM 			if (lro->len & 0x1) {
27910253Sxiuyan.wang@Sun.COM 				/* Odd number of bytes so far, flip bytes */
28010253Sxiuyan.wang@Sun.COM 				csum = ((csum << 8) | (csum >> 8)) & 0xffff;
28110253Sxiuyan.wang@Sun.COM 			}
28210253Sxiuyan.wang@Sun.COM 			csum = csum + lro->data_csum;
28310253Sxiuyan.wang@Sun.COM 			csum = (csum & 0xffff) + (csum >> 16);
28410253Sxiuyan.wang@Sun.COM 			csum = (csum & 0xffff) + (csum >> 16);
28510253Sxiuyan.wang@Sun.COM 			lro->data_csum = csum;
28610253Sxiuyan.wang@Sun.COM 
28710253Sxiuyan.wang@Sun.COM 			lro->len += tcp_data_len;
28810253Sxiuyan.wang@Sun.COM 
28910253Sxiuyan.wang@Sun.COM 			/*
29010253Sxiuyan.wang@Sun.COM 			 * adjust mblk so that rptr points to
29110253Sxiuyan.wang@Sun.COM 			 * the first byte of the payload
29210253Sxiuyan.wang@Sun.COM 			 */
29310253Sxiuyan.wang@Sun.COM 			m_head->b_rptr += hlen;
29410253Sxiuyan.wang@Sun.COM 			/* append mbuf chain */
29510253Sxiuyan.wang@Sun.COM 			lro->m_tail->b_cont = m_head;
29610253Sxiuyan.wang@Sun.COM 			/* advance the last pointer */
29710253Sxiuyan.wang@Sun.COM 			lro->m_tail = m_head;
29810253Sxiuyan.wang@Sun.COM 			/* flush packet if required */
29910253Sxiuyan.wang@Sun.COM 			if (lro->len > (65535 - myri10ge_mtu) ||
30010253Sxiuyan.wang@Sun.COM 			    (lro->append_cnt + 1) == myri10ge_lro_max_aggr) {
30110253Sxiuyan.wang@Sun.COM 				if (ss->lro_active == lro) {
30210253Sxiuyan.wang@Sun.COM 					ss->lro_active = lro->next;
30310253Sxiuyan.wang@Sun.COM 				} else {
30410253Sxiuyan.wang@Sun.COM 					curr = ss->lro_active;
30510253Sxiuyan.wang@Sun.COM 					while (curr->next != lro)
30610253Sxiuyan.wang@Sun.COM 						curr = curr->next;
30710253Sxiuyan.wang@Sun.COM 					curr->next = lro->next;
30810253Sxiuyan.wang@Sun.COM 				}
30910253Sxiuyan.wang@Sun.COM 				myri10ge_lro_flush(ss, lro, mbl);
31010253Sxiuyan.wang@Sun.COM 			}
31110253Sxiuyan.wang@Sun.COM 			return (0);
31210253Sxiuyan.wang@Sun.COM 		}
31310253Sxiuyan.wang@Sun.COM 	}
31410253Sxiuyan.wang@Sun.COM 
31510253Sxiuyan.wang@Sun.COM 	if (ss->lro_free == NULL)
31610253Sxiuyan.wang@Sun.COM 		return (ENOMEM);
31710253Sxiuyan.wang@Sun.COM 
31810253Sxiuyan.wang@Sun.COM 	/* start a new chain */
31910253Sxiuyan.wang@Sun.COM 	lro = ss->lro_free;
32010253Sxiuyan.wang@Sun.COM 	ss->lro_free = lro->next;
32110253Sxiuyan.wang@Sun.COM 	lro->next = ss->lro_active;
32210253Sxiuyan.wang@Sun.COM 	ss->lro_active = lro;
32310253Sxiuyan.wang@Sun.COM 	lro->source_port = tcp->th_sport;
32410253Sxiuyan.wang@Sun.COM 	lro->dest_port = tcp->th_dport;
32510253Sxiuyan.wang@Sun.COM 	lro->source_ip = ip->ip_src.s_addr;
32610253Sxiuyan.wang@Sun.COM 	lro->dest_ip = ip->ip_dst.s_addr;
32710253Sxiuyan.wang@Sun.COM 	lro->next_seq = seq + tcp_data_len;
32810253Sxiuyan.wang@Sun.COM 	lro->mss = (uint16_t)tcp_data_len;
32910253Sxiuyan.wang@Sun.COM 	lro->ack_seq = tcp->th_ack;
33010253Sxiuyan.wang@Sun.COM 	lro->window = tcp->th_win;
33110253Sxiuyan.wang@Sun.COM 	lro->flags = tcp->th_flags;
33210253Sxiuyan.wang@Sun.COM 
33310253Sxiuyan.wang@Sun.COM 	/*
33410253Sxiuyan.wang@Sun.COM 	 * save the checksum of just the TCP payload by
33510253Sxiuyan.wang@Sun.COM 	 * subtracting off the checksum of the TCP header from
33610253Sxiuyan.wang@Sun.COM 	 * the entire hardware checksum
33710253Sxiuyan.wang@Sun.COM 	 * Since IP header checksum is correct, checksum over
33810253Sxiuyan.wang@Sun.COM 	 * the IP header is -0.  Substracting -0 is unnecessary.
33910253Sxiuyan.wang@Sun.COM 	 */
34010253Sxiuyan.wang@Sun.COM 	tmp_csum = myri10ge_csum_generic((uint16_t *)tcp, tcp_hdr_len);
34110253Sxiuyan.wang@Sun.COM 	csum = csum + (tmp_csum ^ 0xffff);
34210253Sxiuyan.wang@Sun.COM 	csum = (csum & 0xffff) + (csum >> 16);
34310253Sxiuyan.wang@Sun.COM 	csum = (csum & 0xffff) + (csum >> 16);
34410253Sxiuyan.wang@Sun.COM 	lro->data_csum = csum;
34510253Sxiuyan.wang@Sun.COM 	lro->ip = ip;
34610253Sxiuyan.wang@Sun.COM 
34710253Sxiuyan.wang@Sun.COM 	/* record timestamp if it is present */
34810253Sxiuyan.wang@Sun.COM 	if (opt_bytes) {
34910253Sxiuyan.wang@Sun.COM 		lro->timestamp = 1;
35010253Sxiuyan.wang@Sun.COM 		lro->tsval = ntohl(*(ts_ptr + 1));
35110253Sxiuyan.wang@Sun.COM 		lro->tsecr = *(ts_ptr + 2);
35210253Sxiuyan.wang@Sun.COM 	}
35310253Sxiuyan.wang@Sun.COM 	lro->len = tot_len;
35410253Sxiuyan.wang@Sun.COM 	lro->m_head = m_head;
35510253Sxiuyan.wang@Sun.COM 	lro->m_tail = m_head;
35610253Sxiuyan.wang@Sun.COM 	return (0);
35710253Sxiuyan.wang@Sun.COM }
35810253Sxiuyan.wang@Sun.COM 
35910253Sxiuyan.wang@Sun.COM /*
36010253Sxiuyan.wang@Sun.COM  *  This file uses MyriGE driver indentation.
36110253Sxiuyan.wang@Sun.COM  *
36210253Sxiuyan.wang@Sun.COM  * Local Variables:
36310253Sxiuyan.wang@Sun.COM  * c-file-style:"sun"
36410253Sxiuyan.wang@Sun.COM  * tab-width:8
36510253Sxiuyan.wang@Sun.COM  * End:
36610253Sxiuyan.wang@Sun.COM  */
367