xref: /onnv-gate/usr/src/uts/common/io/ixgbe/ixgbe_rx.c (revision 13006:22e6d3edaab5)
16621Sbt150084 /*
26621Sbt150084  * CDDL HEADER START
36621Sbt150084  *
46621Sbt150084  * The contents of this file are subject to the terms of the
56621Sbt150084  * Common Development and Distribution License (the "License").
66621Sbt150084  * You may not use this file except in compliance with the License.
76621Sbt150084  *
88275SEric Cheng  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
98275SEric Cheng  * or http://www.opensolaris.org/os/licensing.
106621Sbt150084  * See the License for the specific language governing permissions
116621Sbt150084  * and limitations under the License.
126621Sbt150084  *
138275SEric Cheng  * When distributing Covered Code, include this CDDL HEADER in each
148275SEric Cheng  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156621Sbt150084  * If applicable, add the following below this CDDL HEADER, with the
166621Sbt150084  * fields enclosed by brackets "[]" replaced with your own identifying
176621Sbt150084  * information: Portions Copyright [yyyy] [name of copyright owner]
186621Sbt150084  *
196621Sbt150084  * CDDL HEADER END
206621Sbt150084  */
216621Sbt150084 
226621Sbt150084 /*
23*13006SChenlu.Chen@Sun.COM  * Copyright(c) 2007-2010 Intel Corporation. All rights reserved.
24*13006SChenlu.Chen@Sun.COM  */
25*13006SChenlu.Chen@Sun.COM 
26*13006SChenlu.Chen@Sun.COM /*
27*13006SChenlu.Chen@Sun.COM  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
288275SEric Cheng  */
296621Sbt150084 
306621Sbt150084 #include "ixgbe_sw.h"
316621Sbt150084 
326621Sbt150084 /* function prototypes */
3310376SChenlu.Chen@Sun.COM static mblk_t *ixgbe_rx_bind(ixgbe_rx_data_t *, uint32_t, uint32_t);
3410376SChenlu.Chen@Sun.COM static mblk_t *ixgbe_rx_copy(ixgbe_rx_data_t *, uint32_t, uint32_t);
356621Sbt150084 static void ixgbe_rx_assoc_hcksum(mblk_t *, uint32_t);
3611486SZhen.W@Sun.COM static mblk_t *ixgbe_lro_bind(ixgbe_rx_data_t *, uint32_t, uint32_t, uint32_t);
3711486SZhen.W@Sun.COM static mblk_t *ixgbe_lro_copy(ixgbe_rx_data_t *, uint32_t, uint32_t, uint32_t);
3811486SZhen.W@Sun.COM static int ixgbe_lro_get_start(ixgbe_rx_data_t *, uint32_t);
3911486SZhen.W@Sun.COM static uint32_t ixgbe_lro_get_first(ixgbe_rx_data_t *, uint32_t);
406621Sbt150084 
416621Sbt150084 #ifndef IXGBE_DEBUG
426621Sbt150084 #pragma inline(ixgbe_rx_assoc_hcksum)
4311486SZhen.W@Sun.COM #pragma inline(ixgbe_lro_get_start)
4411486SZhen.W@Sun.COM #pragma inline(ixgbe_lro_get_first)
456621Sbt150084 #endif
466621Sbt150084 
476621Sbt150084 /*
486621Sbt150084  * ixgbe_rx_recycle - The call-back function to reclaim rx buffer.
496621Sbt150084  *
506621Sbt150084  * This function is called when an mp is freed by the user thru
516621Sbt150084  * freeb call (Only for mp constructed through desballoc call).
526621Sbt150084  * It returns back the freed buffer to the free list.
536621Sbt150084  */
546621Sbt150084 void
ixgbe_rx_recycle(caddr_t arg)556621Sbt150084 ixgbe_rx_recycle(caddr_t arg)
566621Sbt150084 {
5710376SChenlu.Chen@Sun.COM 	ixgbe_t *ixgbe;
586621Sbt150084 	ixgbe_rx_ring_t *rx_ring;
5910376SChenlu.Chen@Sun.COM 	ixgbe_rx_data_t	*rx_data;
606621Sbt150084 	rx_control_block_t *recycle_rcb;
616621Sbt150084 	uint32_t free_index;
6210376SChenlu.Chen@Sun.COM 	uint32_t ref_cnt;
636621Sbt150084 
646621Sbt150084 	recycle_rcb = (rx_control_block_t *)(uintptr_t)arg;
6510376SChenlu.Chen@Sun.COM 	rx_data = recycle_rcb->rx_data;
6610376SChenlu.Chen@Sun.COM 	rx_ring = rx_data->rx_ring;
6710376SChenlu.Chen@Sun.COM 	ixgbe = rx_ring->ixgbe;
686621Sbt150084 
6910376SChenlu.Chen@Sun.COM 	if (recycle_rcb->ref_cnt == 0) {
7010376SChenlu.Chen@Sun.COM 		/*
7110376SChenlu.Chen@Sun.COM 		 * This case only happens when rx buffers are being freed
7210376SChenlu.Chen@Sun.COM 		 * in ixgbe_stop() and freemsg() is called.
7310376SChenlu.Chen@Sun.COM 		 */
746621Sbt150084 		return;
7510376SChenlu.Chen@Sun.COM 	}
766621Sbt150084 
776621Sbt150084 	ASSERT(recycle_rcb->mp == NULL);
786621Sbt150084 
796621Sbt150084 	/*
806621Sbt150084 	 * Using the recycled data buffer to generate a new mblk
816621Sbt150084 	 */
826621Sbt150084 	recycle_rcb->mp = desballoc((unsigned char *)
839353SSamuel.Tu@Sun.COM 	    recycle_rcb->rx_buf.address,
849353SSamuel.Tu@Sun.COM 	    recycle_rcb->rx_buf.size,
856621Sbt150084 	    0, &recycle_rcb->free_rtn);
866621Sbt150084 
876621Sbt150084 	/*
886621Sbt150084 	 * Put the recycled rx control block into free list
896621Sbt150084 	 */
9010376SChenlu.Chen@Sun.COM 	mutex_enter(&rx_data->recycle_lock);
916621Sbt150084 
9210376SChenlu.Chen@Sun.COM 	free_index = rx_data->rcb_tail;
9310376SChenlu.Chen@Sun.COM 	ASSERT(rx_data->free_list[free_index] == NULL);
946621Sbt150084 
9510376SChenlu.Chen@Sun.COM 	rx_data->free_list[free_index] = recycle_rcb;
9610376SChenlu.Chen@Sun.COM 	rx_data->rcb_tail = NEXT_INDEX(free_index, 1, rx_data->free_list_size);
976621Sbt150084 
9810376SChenlu.Chen@Sun.COM 	mutex_exit(&rx_data->recycle_lock);
996621Sbt150084 
1006621Sbt150084 	/*
1016621Sbt150084 	 * The atomic operation on the number of the available rx control
1026621Sbt150084 	 * blocks in the free list is used to make the recycling mutual
1036621Sbt150084 	 * exclusive with the receiving.
1046621Sbt150084 	 */
10510376SChenlu.Chen@Sun.COM 	atomic_inc_32(&rx_data->rcb_free);
10610376SChenlu.Chen@Sun.COM 	ASSERT(rx_data->rcb_free <= rx_data->free_list_size);
10710376SChenlu.Chen@Sun.COM 
10810376SChenlu.Chen@Sun.COM 	/*
10910376SChenlu.Chen@Sun.COM 	 * Considering the case that the interface is unplumbed
11010376SChenlu.Chen@Sun.COM 	 * and there are still some buffers held by the upper layer.
11110376SChenlu.Chen@Sun.COM 	 * When the buffer is returned back, we need to free it.
11210376SChenlu.Chen@Sun.COM 	 */
11310376SChenlu.Chen@Sun.COM 	ref_cnt = atomic_dec_32_nv(&recycle_rcb->ref_cnt);
11410376SChenlu.Chen@Sun.COM 	if (ref_cnt == 0) {
11510376SChenlu.Chen@Sun.COM 		if (recycle_rcb->mp != NULL) {
11610376SChenlu.Chen@Sun.COM 			freemsg(recycle_rcb->mp);
11710376SChenlu.Chen@Sun.COM 			recycle_rcb->mp = NULL;
11810376SChenlu.Chen@Sun.COM 		}
11910376SChenlu.Chen@Sun.COM 
12010376SChenlu.Chen@Sun.COM 		ixgbe_free_dma_buffer(&recycle_rcb->rx_buf);
12110376SChenlu.Chen@Sun.COM 
12210376SChenlu.Chen@Sun.COM 		mutex_enter(&ixgbe->rx_pending_lock);
12310376SChenlu.Chen@Sun.COM 		atomic_dec_32(&rx_data->rcb_pending);
12410376SChenlu.Chen@Sun.COM 		atomic_dec_32(&ixgbe->rcb_pending);
12510376SChenlu.Chen@Sun.COM 
12610376SChenlu.Chen@Sun.COM 		/*
12710376SChenlu.Chen@Sun.COM 		 * When there is not any buffer belonging to this rx_data
12810376SChenlu.Chen@Sun.COM 		 * held by the upper layer, the rx_data can be freed.
12910376SChenlu.Chen@Sun.COM 		 */
13010376SChenlu.Chen@Sun.COM 		if ((rx_data->flag & IXGBE_RX_STOPPED) &&
13110376SChenlu.Chen@Sun.COM 		    (rx_data->rcb_pending == 0))
13210376SChenlu.Chen@Sun.COM 			ixgbe_free_rx_ring_data(rx_data);
13310376SChenlu.Chen@Sun.COM 
13410376SChenlu.Chen@Sun.COM 		mutex_exit(&ixgbe->rx_pending_lock);
13510376SChenlu.Chen@Sun.COM 	}
1366621Sbt150084 }
1376621Sbt150084 
1386621Sbt150084 /*
1396621Sbt150084  * ixgbe_rx_copy - Use copy to process the received packet.
1406621Sbt150084  *
1416621Sbt150084  * This function will use bcopy to process the packet
1426621Sbt150084  * and send the copied packet upstream.
1436621Sbt150084  */
1446621Sbt150084 static mblk_t *
ixgbe_rx_copy(ixgbe_rx_data_t * rx_data,uint32_t index,uint32_t pkt_len)14510376SChenlu.Chen@Sun.COM ixgbe_rx_copy(ixgbe_rx_data_t *rx_data, uint32_t index, uint32_t pkt_len)
1466621Sbt150084 {
14710376SChenlu.Chen@Sun.COM 	ixgbe_t *ixgbe;
1486621Sbt150084 	rx_control_block_t *current_rcb;
1496621Sbt150084 	mblk_t *mp;
1506621Sbt150084 
15110376SChenlu.Chen@Sun.COM 	ixgbe = rx_data->rx_ring->ixgbe;
15210376SChenlu.Chen@Sun.COM 	current_rcb = rx_data->work_list[index];
1536621Sbt150084 
1546621Sbt150084 	DMA_SYNC(&current_rcb->rx_buf, DDI_DMA_SYNC_FORKERNEL);
1556621Sbt150084 
1566621Sbt150084 	if (ixgbe_check_dma_handle(current_rcb->rx_buf.dma_handle) !=
1576621Sbt150084 	    DDI_FM_OK) {
15810376SChenlu.Chen@Sun.COM 		ddi_fm_service_impact(ixgbe->dip, DDI_SERVICE_DEGRADED);
15911233SPaul.Guo@Sun.COM 		atomic_or_32(&ixgbe->ixgbe_state, IXGBE_ERROR);
16011233SPaul.Guo@Sun.COM 		return (NULL);
1616621Sbt150084 	}
1626621Sbt150084 
1636621Sbt150084 	/*
1646621Sbt150084 	 * Allocate buffer to receive this packet
1656621Sbt150084 	 */
1666621Sbt150084 	mp = allocb(pkt_len + IPHDR_ALIGN_ROOM, 0);
1676621Sbt150084 	if (mp == NULL) {
16810376SChenlu.Chen@Sun.COM 		ixgbe_log(ixgbe, "ixgbe_rx_copy: allocate buffer failed");
1696621Sbt150084 		return (NULL);
1706621Sbt150084 	}
1716621Sbt150084 
1726621Sbt150084 	/*
1736621Sbt150084 	 * Copy the data received into the new cluster
1746621Sbt150084 	 */
1756621Sbt150084 	mp->b_rptr += IPHDR_ALIGN_ROOM;
1766621Sbt150084 	bcopy(current_rcb->rx_buf.address, mp->b_rptr, pkt_len);
1776621Sbt150084 	mp->b_wptr = mp->b_rptr + pkt_len;
1786621Sbt150084 
1796621Sbt150084 	return (mp);
1806621Sbt150084 }
1816621Sbt150084 
1826621Sbt150084 /*
1836621Sbt150084  * ixgbe_rx_bind - Use existing DMA buffer to build mblk for receiving.
1846621Sbt150084  *
1856621Sbt150084  * This function will use pre-bound DMA buffer to receive the packet
1866621Sbt150084  * and build mblk that will be sent upstream.
1876621Sbt150084  */
1886621Sbt150084 static mblk_t *
ixgbe_rx_bind(ixgbe_rx_data_t * rx_data,uint32_t index,uint32_t pkt_len)18910376SChenlu.Chen@Sun.COM ixgbe_rx_bind(ixgbe_rx_data_t *rx_data, uint32_t index, uint32_t pkt_len)
1906621Sbt150084 {
1916621Sbt150084 	rx_control_block_t *current_rcb;
1926621Sbt150084 	rx_control_block_t *free_rcb;
1936621Sbt150084 	uint32_t free_index;
1946621Sbt150084 	mblk_t *mp;
19510376SChenlu.Chen@Sun.COM 	ixgbe_t	*ixgbe = rx_data->rx_ring->ixgbe;
1966621Sbt150084 
1976621Sbt150084 	/*
1986621Sbt150084 	 * If the free list is empty, we cannot proceed to send
1996621Sbt150084 	 * the current DMA buffer upstream. We'll have to return
2006621Sbt150084 	 * and use bcopy to process the packet.
2016621Sbt150084 	 */
20210376SChenlu.Chen@Sun.COM 	if (ixgbe_atomic_reserve(&rx_data->rcb_free, 1) < 0)
2036621Sbt150084 		return (NULL);
2046621Sbt150084 
20510376SChenlu.Chen@Sun.COM 	current_rcb = rx_data->work_list[index];
2066621Sbt150084 	/*
2076621Sbt150084 	 * If the mp of the rx control block is NULL, try to do
2086621Sbt150084 	 * desballoc again.
2096621Sbt150084 	 */
2106621Sbt150084 	if (current_rcb->mp == NULL) {
2116621Sbt150084 		current_rcb->mp = desballoc((unsigned char *)
2129353SSamuel.Tu@Sun.COM 		    current_rcb->rx_buf.address,
2139353SSamuel.Tu@Sun.COM 		    current_rcb->rx_buf.size,
2146621Sbt150084 		    0, &current_rcb->free_rtn);
2156621Sbt150084 		/*
2166621Sbt150084 		 * If it is failed to built a mblk using the current
2176621Sbt150084 		 * DMA buffer, we have to return and use bcopy to
2186621Sbt150084 		 * process the packet.
2196621Sbt150084 		 */
2209353SSamuel.Tu@Sun.COM 		if (current_rcb->mp == NULL) {
22110376SChenlu.Chen@Sun.COM 			atomic_inc_32(&rx_data->rcb_free);
2226621Sbt150084 			return (NULL);
2236621Sbt150084 		}
2246621Sbt150084 	}
2256621Sbt150084 	/*
2266621Sbt150084 	 * Sync up the data received
2276621Sbt150084 	 */
2286621Sbt150084 	DMA_SYNC(&current_rcb->rx_buf, DDI_DMA_SYNC_FORKERNEL);
2296621Sbt150084 
2306621Sbt150084 	if (ixgbe_check_dma_handle(current_rcb->rx_buf.dma_handle) !=
2316621Sbt150084 	    DDI_FM_OK) {
23210376SChenlu.Chen@Sun.COM 		ddi_fm_service_impact(ixgbe->dip, DDI_SERVICE_DEGRADED);
23311233SPaul.Guo@Sun.COM 		atomic_inc_32(&rx_data->rcb_free);
23411233SPaul.Guo@Sun.COM 		atomic_or_32(&ixgbe->ixgbe_state, IXGBE_ERROR);
23511233SPaul.Guo@Sun.COM 		return (NULL);
2366621Sbt150084 	}
2376621Sbt150084 
2386621Sbt150084 	mp = current_rcb->mp;
2396621Sbt150084 	current_rcb->mp = NULL;
24010376SChenlu.Chen@Sun.COM 	atomic_inc_32(&current_rcb->ref_cnt);
2416621Sbt150084 
2426621Sbt150084 	mp->b_wptr = mp->b_rptr + pkt_len;
2436621Sbt150084 	mp->b_next = mp->b_cont = NULL;
2446621Sbt150084 
2456621Sbt150084 	/*
2466621Sbt150084 	 * Strip off one free rx control block from the free list
2476621Sbt150084 	 */
24810376SChenlu.Chen@Sun.COM 	free_index = rx_data->rcb_head;
24910376SChenlu.Chen@Sun.COM 	free_rcb = rx_data->free_list[free_index];
2506621Sbt150084 	ASSERT(free_rcb != NULL);
25110376SChenlu.Chen@Sun.COM 	rx_data->free_list[free_index] = NULL;
25210376SChenlu.Chen@Sun.COM 	rx_data->rcb_head = NEXT_INDEX(free_index, 1, rx_data->free_list_size);
2536621Sbt150084 
2546621Sbt150084 	/*
2556621Sbt150084 	 * Put the rx control block to the work list
2566621Sbt150084 	 */
25710376SChenlu.Chen@Sun.COM 	rx_data->work_list[index] = free_rcb;
2586621Sbt150084 
2596621Sbt150084 	return (mp);
2606621Sbt150084 }
2616621Sbt150084 
2626621Sbt150084 /*
26311486SZhen.W@Sun.COM  * ixgbe_lro_bind - Use existing DMA buffer to build LRO mblk for receiving.
26411486SZhen.W@Sun.COM  *
26511486SZhen.W@Sun.COM  * This function will use pre-bound DMA buffers to receive the packet
26611486SZhen.W@Sun.COM  * and build LRO mblk that will be sent upstream.
26711486SZhen.W@Sun.COM  */
26811486SZhen.W@Sun.COM static mblk_t *
ixgbe_lro_bind(ixgbe_rx_data_t * rx_data,uint32_t lro_start,uint32_t lro_num,uint32_t pkt_len)26911486SZhen.W@Sun.COM ixgbe_lro_bind(ixgbe_rx_data_t *rx_data, uint32_t lro_start,
27011486SZhen.W@Sun.COM     uint32_t lro_num, uint32_t pkt_len)
27111486SZhen.W@Sun.COM {
27211486SZhen.W@Sun.COM 	rx_control_block_t *current_rcb;
27311486SZhen.W@Sun.COM 	union ixgbe_adv_rx_desc *current_rbd;
27411486SZhen.W@Sun.COM 	rx_control_block_t *free_rcb;
27511486SZhen.W@Sun.COM 	uint32_t free_index;
27611486SZhen.W@Sun.COM 	int lro_next;
27711486SZhen.W@Sun.COM 	uint32_t last_pkt_len;
27811486SZhen.W@Sun.COM 	uint32_t i;
27911486SZhen.W@Sun.COM 	mblk_t *mp;
28011486SZhen.W@Sun.COM 	mblk_t *mblk_head;
28111486SZhen.W@Sun.COM 	mblk_t **mblk_tail;
28211486SZhen.W@Sun.COM 	ixgbe_t	*ixgbe = rx_data->rx_ring->ixgbe;
28311486SZhen.W@Sun.COM 
28411486SZhen.W@Sun.COM 	/*
28511486SZhen.W@Sun.COM 	 * If the free list is empty, we cannot proceed to send
28611486SZhen.W@Sun.COM 	 * the current DMA buffer upstream. We'll have to return
28711486SZhen.W@Sun.COM 	 * and use bcopy to process the packet.
28811486SZhen.W@Sun.COM 	 */
28911486SZhen.W@Sun.COM 	if (ixgbe_atomic_reserve(&rx_data->rcb_free, lro_num) < 0)
29011486SZhen.W@Sun.COM 		return (NULL);
29111486SZhen.W@Sun.COM 	current_rcb = rx_data->work_list[lro_start];
29211486SZhen.W@Sun.COM 
29311486SZhen.W@Sun.COM 	/*
29411486SZhen.W@Sun.COM 	 * If any one of the rx data blocks can not support
29511486SZhen.W@Sun.COM 	 * lro bind  operation,  We'll have to return and use
29611486SZhen.W@Sun.COM 	 * bcopy to process the lro  packet.
29711486SZhen.W@Sun.COM 	 */
29811486SZhen.W@Sun.COM 	for (i = lro_num; i > 0; i--) {
29911486SZhen.W@Sun.COM 		/*
30011486SZhen.W@Sun.COM 		 * Sync up the data received
30111486SZhen.W@Sun.COM 		 */
30211486SZhen.W@Sun.COM 		DMA_SYNC(&current_rcb->rx_buf, DDI_DMA_SYNC_FORKERNEL);
30311486SZhen.W@Sun.COM 
30411486SZhen.W@Sun.COM 		if (ixgbe_check_dma_handle(current_rcb->rx_buf.dma_handle) !=
30511486SZhen.W@Sun.COM 		    DDI_FM_OK) {
30611486SZhen.W@Sun.COM 			ddi_fm_service_impact(ixgbe->dip, DDI_SERVICE_DEGRADED);
30711486SZhen.W@Sun.COM 			atomic_add_32(&rx_data->rcb_free, lro_num);
30811486SZhen.W@Sun.COM 			atomic_or_32(&ixgbe->ixgbe_state, IXGBE_ERROR);
30911486SZhen.W@Sun.COM 			return (NULL);
31011486SZhen.W@Sun.COM 		}
31111486SZhen.W@Sun.COM 
31211486SZhen.W@Sun.COM 		/*
31311486SZhen.W@Sun.COM 		 * If the mp of the rx control block is NULL, try to do
31411486SZhen.W@Sun.COM 		 * desballoc again.
31511486SZhen.W@Sun.COM 		 */
31611486SZhen.W@Sun.COM 		if (current_rcb->mp == NULL) {
31711486SZhen.W@Sun.COM 			current_rcb->mp = desballoc((unsigned char *)
31811486SZhen.W@Sun.COM 			    current_rcb->rx_buf.address,
31911486SZhen.W@Sun.COM 			    current_rcb->rx_buf.size,
32011486SZhen.W@Sun.COM 			    0, &current_rcb->free_rtn);
32111486SZhen.W@Sun.COM 			/*
32211486SZhen.W@Sun.COM 			 * If it is failed to built a mblk using the current
32311486SZhen.W@Sun.COM 			 * DMA buffer, we have to return and use bcopy to
32411486SZhen.W@Sun.COM 			 * process the packet.
32511486SZhen.W@Sun.COM 			 */
32611486SZhen.W@Sun.COM 			if (current_rcb->mp == NULL) {
32711486SZhen.W@Sun.COM 				atomic_add_32(&rx_data->rcb_free, lro_num);
32811486SZhen.W@Sun.COM 				return (NULL);
32911486SZhen.W@Sun.COM 			}
33011486SZhen.W@Sun.COM 		}
33111486SZhen.W@Sun.COM 		if (current_rcb->lro_next != -1)
33211486SZhen.W@Sun.COM 			lro_next = current_rcb->lro_next;
33311486SZhen.W@Sun.COM 		current_rcb = rx_data->work_list[lro_next];
33411486SZhen.W@Sun.COM 	}
33511486SZhen.W@Sun.COM 
33611486SZhen.W@Sun.COM 	mblk_head = NULL;
33711486SZhen.W@Sun.COM 	mblk_tail = &mblk_head;
33811486SZhen.W@Sun.COM 	lro_next = lro_start;
33911486SZhen.W@Sun.COM 	last_pkt_len = pkt_len - ixgbe->rx_buf_size * (lro_num - 1);
34011486SZhen.W@Sun.COM 	current_rcb = rx_data->work_list[lro_next];
34111486SZhen.W@Sun.COM 	current_rbd = &rx_data->rbd_ring[lro_next];
34211486SZhen.W@Sun.COM 	while (lro_num --) {
34311486SZhen.W@Sun.COM 		mp = current_rcb->mp;
34411486SZhen.W@Sun.COM 		current_rcb->mp = NULL;
34511486SZhen.W@Sun.COM 		atomic_inc_32(&current_rcb->ref_cnt);
34611486SZhen.W@Sun.COM 		if (lro_num != 0)
34711486SZhen.W@Sun.COM 			mp->b_wptr = mp->b_rptr + ixgbe->rx_buf_size;
34811486SZhen.W@Sun.COM 		else
34911486SZhen.W@Sun.COM 			mp->b_wptr = mp->b_rptr + last_pkt_len;
35011486SZhen.W@Sun.COM 		mp->b_next = mp->b_cont = NULL;
35111486SZhen.W@Sun.COM 		*mblk_tail = mp;
35211486SZhen.W@Sun.COM 		mblk_tail = &mp->b_cont;
35311486SZhen.W@Sun.COM 
35411486SZhen.W@Sun.COM 		/*
35511486SZhen.W@Sun.COM 		 * Strip off one free rx control block from the free list
35611486SZhen.W@Sun.COM 		 */
35711486SZhen.W@Sun.COM 		free_index = rx_data->rcb_head;
35811486SZhen.W@Sun.COM 		free_rcb = rx_data->free_list[free_index];
35911486SZhen.W@Sun.COM 		ASSERT(free_rcb != NULL);
36011486SZhen.W@Sun.COM 		rx_data->free_list[free_index] = NULL;
36111486SZhen.W@Sun.COM 		rx_data->rcb_head = NEXT_INDEX(free_index, 1,
36211486SZhen.W@Sun.COM 		    rx_data->free_list_size);
36311486SZhen.W@Sun.COM 
36411486SZhen.W@Sun.COM 		/*
36511486SZhen.W@Sun.COM 		 * Put the rx control block to the work list
36611486SZhen.W@Sun.COM 		 */
36711486SZhen.W@Sun.COM 		rx_data->work_list[lro_next] = free_rcb;
36811486SZhen.W@Sun.COM 		lro_next = current_rcb->lro_next;
36911486SZhen.W@Sun.COM 		current_rcb->lro_next = -1;
37011486SZhen.W@Sun.COM 		current_rcb->lro_prev = -1;
37111486SZhen.W@Sun.COM 		current_rcb->lro_pkt = B_FALSE;
37211486SZhen.W@Sun.COM 		current_rbd->read.pkt_addr = free_rcb->rx_buf.dma_address;
37311486SZhen.W@Sun.COM 		current_rbd->read.hdr_addr = 0;
37411486SZhen.W@Sun.COM 		if (lro_next == -1)
37511486SZhen.W@Sun.COM 			break;
37611486SZhen.W@Sun.COM 		current_rcb = rx_data->work_list[lro_next];
37711486SZhen.W@Sun.COM 		current_rbd = &rx_data->rbd_ring[lro_next];
37811486SZhen.W@Sun.COM 	}
37911486SZhen.W@Sun.COM 	return (mblk_head);
38011486SZhen.W@Sun.COM }
38111486SZhen.W@Sun.COM 
38211486SZhen.W@Sun.COM /*
38311486SZhen.W@Sun.COM  * ixgbe_lro_copy - Use copy to process the received LRO packet.
38411486SZhen.W@Sun.COM  *
38511486SZhen.W@Sun.COM  * This function will use bcopy to process the LRO  packet
38611486SZhen.W@Sun.COM  * and send the copied packet upstream.
38711486SZhen.W@Sun.COM  */
38811486SZhen.W@Sun.COM static mblk_t *
ixgbe_lro_copy(ixgbe_rx_data_t * rx_data,uint32_t lro_start,uint32_t lro_num,uint32_t pkt_len)38911486SZhen.W@Sun.COM ixgbe_lro_copy(ixgbe_rx_data_t *rx_data, uint32_t lro_start,
39011486SZhen.W@Sun.COM     uint32_t lro_num, uint32_t pkt_len)
39111486SZhen.W@Sun.COM {
39211486SZhen.W@Sun.COM 	ixgbe_t *ixgbe;
39311486SZhen.W@Sun.COM 	rx_control_block_t *current_rcb;
39411486SZhen.W@Sun.COM 	union ixgbe_adv_rx_desc *current_rbd;
39511486SZhen.W@Sun.COM 	mblk_t *mp;
39611486SZhen.W@Sun.COM 	uint32_t last_pkt_len;
39711486SZhen.W@Sun.COM 	int lro_next;
39811486SZhen.W@Sun.COM 	uint32_t i;
39911486SZhen.W@Sun.COM 
40011486SZhen.W@Sun.COM 	ixgbe = rx_data->rx_ring->ixgbe;
40111486SZhen.W@Sun.COM 
40211486SZhen.W@Sun.COM 	/*
40311486SZhen.W@Sun.COM 	 * Allocate buffer to receive this LRO packet
40411486SZhen.W@Sun.COM 	 */
40511486SZhen.W@Sun.COM 	mp = allocb(pkt_len + IPHDR_ALIGN_ROOM, 0);
40611486SZhen.W@Sun.COM 	if (mp == NULL) {
40711486SZhen.W@Sun.COM 		ixgbe_log(ixgbe, "LRO copy MP alloc failed");
40811486SZhen.W@Sun.COM 		return (NULL);
40911486SZhen.W@Sun.COM 	}
41011486SZhen.W@Sun.COM 
41111486SZhen.W@Sun.COM 	current_rcb = rx_data->work_list[lro_start];
41211486SZhen.W@Sun.COM 
41311486SZhen.W@Sun.COM 	/*
41411486SZhen.W@Sun.COM 	 * Sync up the LRO packet data received
41511486SZhen.W@Sun.COM 	 */
41611486SZhen.W@Sun.COM 	for (i = lro_num; i > 0; i--) {
41711486SZhen.W@Sun.COM 		DMA_SYNC(&current_rcb->rx_buf, DDI_DMA_SYNC_FORKERNEL);
41811486SZhen.W@Sun.COM 
41911486SZhen.W@Sun.COM 		if (ixgbe_check_dma_handle(current_rcb->rx_buf.dma_handle) !=
42011486SZhen.W@Sun.COM 		    DDI_FM_OK) {
42111486SZhen.W@Sun.COM 			ddi_fm_service_impact(ixgbe->dip, DDI_SERVICE_DEGRADED);
42211486SZhen.W@Sun.COM 			atomic_or_32(&ixgbe->ixgbe_state, IXGBE_ERROR);
42311486SZhen.W@Sun.COM 			return (NULL);
42411486SZhen.W@Sun.COM 		}
42511486SZhen.W@Sun.COM 		if (current_rcb->lro_next != -1)
42611486SZhen.W@Sun.COM 			lro_next = current_rcb->lro_next;
42711486SZhen.W@Sun.COM 		current_rcb = rx_data->work_list[lro_next];
42811486SZhen.W@Sun.COM 	}
42911486SZhen.W@Sun.COM 	lro_next = lro_start;
43011486SZhen.W@Sun.COM 	current_rcb = rx_data->work_list[lro_next];
43111486SZhen.W@Sun.COM 	current_rbd = &rx_data->rbd_ring[lro_next];
43211486SZhen.W@Sun.COM 	last_pkt_len = pkt_len - ixgbe->rx_buf_size * (lro_num - 1);
43311486SZhen.W@Sun.COM 
43411486SZhen.W@Sun.COM 	/*
43511486SZhen.W@Sun.COM 	 * Copy the data received into the new cluster
43611486SZhen.W@Sun.COM 	 */
43711486SZhen.W@Sun.COM 	mp->b_rptr += IPHDR_ALIGN_ROOM;
43811486SZhen.W@Sun.COM 	mp->b_wptr += IPHDR_ALIGN_ROOM;
43911486SZhen.W@Sun.COM 	while (lro_num --) {
44011486SZhen.W@Sun.COM 		if (lro_num != 0) {
44111486SZhen.W@Sun.COM 			bcopy(current_rcb->rx_buf.address, mp->b_wptr,
44211486SZhen.W@Sun.COM 			    ixgbe->rx_buf_size);
44311486SZhen.W@Sun.COM 			mp->b_wptr += ixgbe->rx_buf_size;
44411486SZhen.W@Sun.COM 		} else {
44511486SZhen.W@Sun.COM 			bcopy(current_rcb->rx_buf.address, mp->b_wptr,
44611486SZhen.W@Sun.COM 			    last_pkt_len);
44711486SZhen.W@Sun.COM 			mp->b_wptr += last_pkt_len;
44811486SZhen.W@Sun.COM 		}
44911486SZhen.W@Sun.COM 		lro_next = current_rcb->lro_next;
45011486SZhen.W@Sun.COM 		current_rcb->lro_next = -1;
45111486SZhen.W@Sun.COM 		current_rcb->lro_prev = -1;
45211486SZhen.W@Sun.COM 		current_rcb->lro_pkt = B_FALSE;
45311486SZhen.W@Sun.COM 		current_rbd->read.pkt_addr = current_rcb->rx_buf.dma_address;
45411486SZhen.W@Sun.COM 		current_rbd->read.hdr_addr = 0;
45511486SZhen.W@Sun.COM 		if (lro_next == -1)
45611486SZhen.W@Sun.COM 			break;
45711486SZhen.W@Sun.COM 		current_rcb = rx_data->work_list[lro_next];
45811486SZhen.W@Sun.COM 		current_rbd = &rx_data->rbd_ring[lro_next];
45911486SZhen.W@Sun.COM 	}
46011486SZhen.W@Sun.COM 
46111486SZhen.W@Sun.COM 	return (mp);
46211486SZhen.W@Sun.COM }
46311486SZhen.W@Sun.COM 
46411486SZhen.W@Sun.COM /*
46511486SZhen.W@Sun.COM  * ixgbe_lro_get_start - get the start rcb index in one LRO packet
46611486SZhen.W@Sun.COM  */
46711486SZhen.W@Sun.COM static int
ixgbe_lro_get_start(ixgbe_rx_data_t * rx_data,uint32_t rx_next)46811486SZhen.W@Sun.COM ixgbe_lro_get_start(ixgbe_rx_data_t *rx_data, uint32_t rx_next)
46911486SZhen.W@Sun.COM {
47011486SZhen.W@Sun.COM 	int lro_prev;
47111486SZhen.W@Sun.COM 	int lro_start;
47211486SZhen.W@Sun.COM 	uint32_t lro_num = 1;
47311486SZhen.W@Sun.COM 	rx_control_block_t *prev_rcb;
47411486SZhen.W@Sun.COM 	rx_control_block_t *current_rcb = rx_data->work_list[rx_next];
47511486SZhen.W@Sun.COM 	lro_prev = current_rcb->lro_prev;
47611486SZhen.W@Sun.COM 
47711486SZhen.W@Sun.COM 	while (lro_prev != -1) {
47811486SZhen.W@Sun.COM 		lro_num ++;
47911486SZhen.W@Sun.COM 		prev_rcb = rx_data->work_list[lro_prev];
48011486SZhen.W@Sun.COM 		lro_start = lro_prev;
48111486SZhen.W@Sun.COM 		lro_prev = prev_rcb->lro_prev;
48211486SZhen.W@Sun.COM 	}
48311486SZhen.W@Sun.COM 	rx_data->lro_num = lro_num;
48411486SZhen.W@Sun.COM 	return (lro_start);
48511486SZhen.W@Sun.COM }
48611486SZhen.W@Sun.COM 
48711486SZhen.W@Sun.COM /*
48811486SZhen.W@Sun.COM  * ixgbe_lro_get_first - get the first LRO rcb index
48911486SZhen.W@Sun.COM  */
49011486SZhen.W@Sun.COM static uint32_t
ixgbe_lro_get_first(ixgbe_rx_data_t * rx_data,uint32_t rx_next)49111486SZhen.W@Sun.COM ixgbe_lro_get_first(ixgbe_rx_data_t *rx_data, uint32_t rx_next)
49211486SZhen.W@Sun.COM {
49311486SZhen.W@Sun.COM 	rx_control_block_t *current_rcb;
49411486SZhen.W@Sun.COM 	uint32_t lro_first;
49511486SZhen.W@Sun.COM 	lro_first = rx_data->lro_first;
49611486SZhen.W@Sun.COM 	current_rcb = rx_data->work_list[lro_first];
49711486SZhen.W@Sun.COM 	while ((!current_rcb->lro_pkt) && (lro_first != rx_next)) {
49811486SZhen.W@Sun.COM 		lro_first =  NEXT_INDEX(lro_first, 1, rx_data->ring_size);
49911486SZhen.W@Sun.COM 		current_rcb = rx_data->work_list[lro_first];
50011486SZhen.W@Sun.COM 	}
50111486SZhen.W@Sun.COM 	rx_data->lro_first = lro_first;
50211486SZhen.W@Sun.COM 	return (lro_first);
50311486SZhen.W@Sun.COM }
50411486SZhen.W@Sun.COM 
50511486SZhen.W@Sun.COM /*
5066621Sbt150084  * ixgbe_rx_assoc_hcksum - Check the rx hardware checksum status and associate
5076621Sbt150084  * the hcksum flags.
5086621Sbt150084  */
5096621Sbt150084 static void
ixgbe_rx_assoc_hcksum(mblk_t * mp,uint32_t status_error)5106621Sbt150084 ixgbe_rx_assoc_hcksum(mblk_t *mp, uint32_t status_error)
5116621Sbt150084 {
5126621Sbt150084 	uint32_t hcksum_flags = 0;
5136621Sbt150084 
5146621Sbt150084 	/*
5156621Sbt150084 	 * Check TCP/UDP checksum
5166621Sbt150084 	 */
5176621Sbt150084 	if ((status_error & IXGBE_RXD_STAT_L4CS) &&
5186621Sbt150084 	    !(status_error & IXGBE_RXDADV_ERR_TCPE))
51911878SVenu.Iyer@Sun.COM 		hcksum_flags |= HCK_FULLCKSUM_OK;
5206621Sbt150084 
5216621Sbt150084 	/*
5226621Sbt150084 	 * Check IP Checksum
5236621Sbt150084 	 */
5246621Sbt150084 	if ((status_error & IXGBE_RXD_STAT_IPCS) &&
5256621Sbt150084 	    !(status_error & IXGBE_RXDADV_ERR_IPE))
52611878SVenu.Iyer@Sun.COM 		hcksum_flags |= HCK_IPV4_HDRCKSUM_OK;
5276621Sbt150084 
5286621Sbt150084 	if (hcksum_flags != 0) {
52911878SVenu.Iyer@Sun.COM 		mac_hcksum_set(mp, 0, 0, 0, 0, hcksum_flags);
5306621Sbt150084 	}
5316621Sbt150084 }
5326621Sbt150084 
5336621Sbt150084 /*
5348275SEric Cheng  * ixgbe_ring_rx - Receive the data of one ring.
5356621Sbt150084  *
5366621Sbt150084  * This function goes throught h/w descriptor in one specified rx ring,
5376621Sbt150084  * receives the data if the descriptor status shows the data is ready.
5386621Sbt150084  * It returns a chain of mblks containing the received data, to be
5396621Sbt150084  * passed up to mac_rx().
5406621Sbt150084  */
5416621Sbt150084 mblk_t *
ixgbe_ring_rx(ixgbe_rx_ring_t * rx_ring,int poll_bytes)5428275SEric Cheng ixgbe_ring_rx(ixgbe_rx_ring_t *rx_ring, int poll_bytes)
5436621Sbt150084 {
5446621Sbt150084 	union ixgbe_adv_rx_desc *current_rbd;
5456621Sbt150084 	rx_control_block_t *current_rcb;
5466621Sbt150084 	mblk_t *mp;
5476621Sbt150084 	mblk_t *mblk_head;
5486621Sbt150084 	mblk_t **mblk_tail;
5496621Sbt150084 	uint32_t rx_next;
5506621Sbt150084 	uint32_t rx_tail;
5516621Sbt150084 	uint32_t pkt_len;
5526621Sbt150084 	uint32_t status_error;
5536621Sbt150084 	uint32_t pkt_num;
55411486SZhen.W@Sun.COM 	uint32_t rsc_cnt;
55511486SZhen.W@Sun.COM 	uint32_t lro_first;
55611486SZhen.W@Sun.COM 	uint32_t lro_start;
55711486SZhen.W@Sun.COM 	uint32_t lro_next;
55811486SZhen.W@Sun.COM 	boolean_t lro_eop;
5598275SEric Cheng 	uint32_t received_bytes;
5606621Sbt150084 	ixgbe_t *ixgbe = rx_ring->ixgbe;
56111486SZhen.W@Sun.COM 	ixgbe_rx_data_t *rx_data;
5626621Sbt150084 
56311233SPaul.Guo@Sun.COM 	if ((ixgbe->ixgbe_state & IXGBE_SUSPENDED) ||
56411233SPaul.Guo@Sun.COM 	    (ixgbe->ixgbe_state & IXGBE_ERROR) ||
565*13006SChenlu.Chen@Sun.COM 	    (ixgbe->ixgbe_state & IXGBE_OVERTEMP) ||
56611233SPaul.Guo@Sun.COM 	    !(ixgbe->ixgbe_state & IXGBE_STARTED))
56711233SPaul.Guo@Sun.COM 		return (NULL);
56811233SPaul.Guo@Sun.COM 
56911486SZhen.W@Sun.COM 	rx_data = rx_ring->rx_data;
57011486SZhen.W@Sun.COM 	lro_eop = B_FALSE;
5716621Sbt150084 	mblk_head = NULL;
5726621Sbt150084 	mblk_tail = &mblk_head;
5736621Sbt150084 
5746621Sbt150084 	/*
5756621Sbt150084 	 * Sync the receive descriptors before accepting the packets
5766621Sbt150084 	 */
57710376SChenlu.Chen@Sun.COM 	DMA_SYNC(&rx_data->rbd_area, DDI_DMA_SYNC_FORKERNEL);
5786621Sbt150084 
57910376SChenlu.Chen@Sun.COM 	if (ixgbe_check_dma_handle(rx_data->rbd_area.dma_handle) != DDI_FM_OK) {
58010376SChenlu.Chen@Sun.COM 		ddi_fm_service_impact(ixgbe->dip, DDI_SERVICE_DEGRADED);
58111233SPaul.Guo@Sun.COM 		atomic_or_32(&ixgbe->ixgbe_state, IXGBE_ERROR);
58211233SPaul.Guo@Sun.COM 		return (NULL);
5836621Sbt150084 	}
5846621Sbt150084 
5856621Sbt150084 	/*
5866621Sbt150084 	 * Get the start point of rx bd ring which should be examined
5876621Sbt150084 	 * during this cycle.
5886621Sbt150084 	 */
58910376SChenlu.Chen@Sun.COM 	rx_next = rx_data->rbd_next;
59010376SChenlu.Chen@Sun.COM 	current_rbd = &rx_data->rbd_ring[rx_next];
5918275SEric Cheng 	received_bytes = 0;
5926621Sbt150084 	pkt_num = 0;
5936621Sbt150084 	status_error = current_rbd->wb.upper.status_error;
5946621Sbt150084 	while (status_error & IXGBE_RXD_STAT_DD) {
5956621Sbt150084 		/*
5966621Sbt150084 		 * If adapter has found errors, but the error
5976621Sbt150084 		 * is hardware checksum error, this does not discard the
5986621Sbt150084 		 * packet: let upper layer compute the checksum;
5996621Sbt150084 		 * Otherwise discard the packet.
6006621Sbt150084 		 */
6016621Sbt150084 		if ((status_error & IXGBE_RXDADV_ERR_FRAME_ERR_MASK) ||
60211486SZhen.W@Sun.COM 		    ((!ixgbe->lro_enable) &&
60311486SZhen.W@Sun.COM 		    (!(status_error & IXGBE_RXD_STAT_EOP)))) {
6046621Sbt150084 			IXGBE_DEBUG_STAT(rx_ring->stat_frame_error);
6056621Sbt150084 			goto rx_discard;
6066621Sbt150084 		}
6076621Sbt150084 
6086621Sbt150084 		IXGBE_DEBUG_STAT_COND(rx_ring->stat_cksum_error,
6096621Sbt150084 		    (status_error & IXGBE_RXDADV_ERR_TCPE) ||
6106621Sbt150084 		    (status_error & IXGBE_RXDADV_ERR_IPE));
6116621Sbt150084 
61211486SZhen.W@Sun.COM 		if (ixgbe->lro_enable) {
61311486SZhen.W@Sun.COM 			rsc_cnt =  (current_rbd->wb.lower.lo_dword.data &
61411486SZhen.W@Sun.COM 			    IXGBE_RXDADV_RSCCNT_MASK) >>
61511486SZhen.W@Sun.COM 			    IXGBE_RXDADV_RSCCNT_SHIFT;
61611486SZhen.W@Sun.COM 			if (rsc_cnt != 0) {
61711486SZhen.W@Sun.COM 				if (status_error & IXGBE_RXD_STAT_EOP) {
61811486SZhen.W@Sun.COM 					pkt_len = current_rbd->wb.upper.length;
61911486SZhen.W@Sun.COM 					if (rx_data->work_list[rx_next]->
62011486SZhen.W@Sun.COM 					    lro_prev != -1) {
62111486SZhen.W@Sun.COM 						lro_start =
62211486SZhen.W@Sun.COM 						    ixgbe_lro_get_start(rx_data,
62311486SZhen.W@Sun.COM 						    rx_next);
62411486SZhen.W@Sun.COM 						ixgbe->lro_pkt_count++;
62511486SZhen.W@Sun.COM 						pkt_len +=
62611486SZhen.W@Sun.COM 						    (rx_data->lro_num  - 1) *
62711486SZhen.W@Sun.COM 						    ixgbe->rx_buf_size;
62811486SZhen.W@Sun.COM 						lro_eop = B_TRUE;
62911486SZhen.W@Sun.COM 					}
63011486SZhen.W@Sun.COM 				} else {
63111486SZhen.W@Sun.COM 					lro_next = (status_error &
63211486SZhen.W@Sun.COM 					    IXGBE_RXDADV_NEXTP_MASK) >>
63311486SZhen.W@Sun.COM 					    IXGBE_RXDADV_NEXTP_SHIFT;
63411486SZhen.W@Sun.COM 					rx_data->work_list[lro_next]->lro_prev
63511486SZhen.W@Sun.COM 					    = rx_next;
63611486SZhen.W@Sun.COM 					rx_data->work_list[rx_next]->lro_next =
63711486SZhen.W@Sun.COM 					    lro_next;
63811486SZhen.W@Sun.COM 					rx_data->work_list[rx_next]->lro_pkt =
63911486SZhen.W@Sun.COM 					    B_TRUE;
64011486SZhen.W@Sun.COM 					goto rx_discard;
64111486SZhen.W@Sun.COM 				}
64211486SZhen.W@Sun.COM 
64311486SZhen.W@Sun.COM 			} else {
64411486SZhen.W@Sun.COM 				pkt_len = current_rbd->wb.upper.length;
64511486SZhen.W@Sun.COM 			}
64611486SZhen.W@Sun.COM 		} else {
64711486SZhen.W@Sun.COM 			pkt_len = current_rbd->wb.upper.length;
64811486SZhen.W@Sun.COM 		}
64911486SZhen.W@Sun.COM 
6508275SEric Cheng 
6518275SEric Cheng 		if ((poll_bytes != IXGBE_POLL_NULL) &&
6528275SEric Cheng 		    ((received_bytes + pkt_len) > poll_bytes))
6538275SEric Cheng 			break;
6548275SEric Cheng 
6558275SEric Cheng 		received_bytes += pkt_len;
65611486SZhen.W@Sun.COM 		mp = NULL;
6578275SEric Cheng 
6586621Sbt150084 		/*
6596621Sbt150084 		 * For packets with length more than the copy threshold,
6606621Sbt150084 		 * we'll first try to use the existing DMA buffer to build
6616621Sbt150084 		 * an mblk and send the mblk upstream.
6626621Sbt150084 		 *
6636621Sbt150084 		 * If the first method fails, or the packet length is less
6646621Sbt150084 		 * than the copy threshold, we'll allocate a new mblk and
6656621Sbt150084 		 * copy the packet data to the new mblk.
6666621Sbt150084 		 */
66711486SZhen.W@Sun.COM 		if (lro_eop) {
66811486SZhen.W@Sun.COM 			mp = ixgbe_lro_bind(rx_data, lro_start,
66911486SZhen.W@Sun.COM 			    rx_data->lro_num, pkt_len);
67011486SZhen.W@Sun.COM 			if (mp == NULL)
67111486SZhen.W@Sun.COM 				mp = ixgbe_lro_copy(rx_data, lro_start,
67211486SZhen.W@Sun.COM 				    rx_data->lro_num, pkt_len);
67311486SZhen.W@Sun.COM 			lro_eop = B_FALSE;
67411486SZhen.W@Sun.COM 			rx_data->lro_num = 0;
6756621Sbt150084 
67611486SZhen.W@Sun.COM 		} else {
67711486SZhen.W@Sun.COM 			if (pkt_len > ixgbe->rx_copy_thresh)
67811486SZhen.W@Sun.COM 				mp = ixgbe_rx_bind(rx_data, rx_next, pkt_len);
6796621Sbt150084 
68011486SZhen.W@Sun.COM 			if (mp == NULL)
68111486SZhen.W@Sun.COM 				mp = ixgbe_rx_copy(rx_data, rx_next, pkt_len);
68211486SZhen.W@Sun.COM 		}
6836621Sbt150084 		if (mp != NULL) {
6846621Sbt150084 			/*
6856621Sbt150084 			 * Check h/w checksum offload status
6866621Sbt150084 			 */
6876621Sbt150084 			if (ixgbe->rx_hcksum_enable)
6886621Sbt150084 				ixgbe_rx_assoc_hcksum(mp, status_error);
6896621Sbt150084 
6906621Sbt150084 			*mblk_tail = mp;
6916621Sbt150084 			mblk_tail = &mp->b_next;
6926621Sbt150084 		}
6936621Sbt150084 
6946621Sbt150084 rx_discard:
6956621Sbt150084 		/*
6966621Sbt150084 		 * Reset rx descriptor read bits
6976621Sbt150084 		 */
69810376SChenlu.Chen@Sun.COM 		current_rcb = rx_data->work_list[rx_next];
69911486SZhen.W@Sun.COM 		if (ixgbe->lro_enable) {
70011486SZhen.W@Sun.COM 			if (!current_rcb->lro_pkt) {
70111486SZhen.W@Sun.COM 				current_rbd->read.pkt_addr =
70211486SZhen.W@Sun.COM 				    current_rcb->rx_buf.dma_address;
70311486SZhen.W@Sun.COM 				current_rbd->read.hdr_addr = 0;
70411486SZhen.W@Sun.COM 			}
70511486SZhen.W@Sun.COM 		} else {
70611486SZhen.W@Sun.COM 			current_rbd->read.pkt_addr =
70711486SZhen.W@Sun.COM 			    current_rcb->rx_buf.dma_address;
70811486SZhen.W@Sun.COM 			current_rbd->read.hdr_addr = 0;
70911486SZhen.W@Sun.COM 		}
7106621Sbt150084 
71110376SChenlu.Chen@Sun.COM 		rx_next = NEXT_INDEX(rx_next, 1, rx_data->ring_size);
7126621Sbt150084 
7136621Sbt150084 		/*
7146621Sbt150084 		 * The receive function is in interrupt context, so here
71510376SChenlu.Chen@Sun.COM 		 * rx_limit_per_intr is used to avoid doing receiving too long
7166621Sbt150084 		 * per interrupt.
7176621Sbt150084 		 */
71810376SChenlu.Chen@Sun.COM 		if (++pkt_num > ixgbe->rx_limit_per_intr) {
7196621Sbt150084 			IXGBE_DEBUG_STAT(rx_ring->stat_exceed_pkt);
7206621Sbt150084 			break;
7216621Sbt150084 		}
7226621Sbt150084 
72310376SChenlu.Chen@Sun.COM 		current_rbd = &rx_data->rbd_ring[rx_next];
7246621Sbt150084 		status_error = current_rbd->wb.upper.status_error;
7256621Sbt150084 	}
7266621Sbt150084 
72711878SVenu.Iyer@Sun.COM 	rx_ring->stat_rbytes += received_bytes;
72811878SVenu.Iyer@Sun.COM 	rx_ring->stat_ipackets += pkt_num;
72911878SVenu.Iyer@Sun.COM 
73010376SChenlu.Chen@Sun.COM 	DMA_SYNC(&rx_data->rbd_area, DDI_DMA_SYNC_FORDEV);
7316621Sbt150084 
73210376SChenlu.Chen@Sun.COM 	rx_data->rbd_next = rx_next;
7336621Sbt150084 
7346621Sbt150084 	/*
7356621Sbt150084 	 * Update the h/w tail accordingly
7366621Sbt150084 	 */
73711486SZhen.W@Sun.COM 	if (ixgbe->lro_enable) {
73811486SZhen.W@Sun.COM 		lro_first = ixgbe_lro_get_first(rx_data, rx_next);
73911486SZhen.W@Sun.COM 		rx_tail = PREV_INDEX(lro_first, 1, rx_data->ring_size);
74011486SZhen.W@Sun.COM 	} else
74111486SZhen.W@Sun.COM 		rx_tail = PREV_INDEX(rx_next, 1, rx_data->ring_size);
74211486SZhen.W@Sun.COM 
74311878SVenu.Iyer@Sun.COM 	IXGBE_WRITE_REG(&ixgbe->hw, IXGBE_RDT(rx_ring->hw_index), rx_tail);
7446621Sbt150084 
7456621Sbt150084 	if (ixgbe_check_acc_handle(ixgbe->osdep.reg_handle) != DDI_FM_OK) {
74610376SChenlu.Chen@Sun.COM 		ddi_fm_service_impact(ixgbe->dip, DDI_SERVICE_DEGRADED);
74711233SPaul.Guo@Sun.COM 		atomic_or_32(&ixgbe->ixgbe_state, IXGBE_ERROR);
7486621Sbt150084 	}
7496621Sbt150084 
7506621Sbt150084 	return (mblk_head);
7516621Sbt150084 }
7528275SEric Cheng 
7538275SEric Cheng mblk_t *
ixgbe_ring_rx_poll(void * arg,int n_bytes)7548275SEric Cheng ixgbe_ring_rx_poll(void *arg, int n_bytes)
7558275SEric Cheng {
7568275SEric Cheng 	ixgbe_rx_ring_t *rx_ring = (ixgbe_rx_ring_t *)arg;
7578275SEric Cheng 	mblk_t *mp = NULL;
7588275SEric Cheng 
7598275SEric Cheng 	ASSERT(n_bytes >= 0);
7608275SEric Cheng 
7618275SEric Cheng 	if (n_bytes == 0)
76211233SPaul.Guo@Sun.COM 		return (NULL);
7638275SEric Cheng 
7648275SEric Cheng 	mutex_enter(&rx_ring->rx_lock);
7658275SEric Cheng 	mp = ixgbe_ring_rx(rx_ring, n_bytes);
7668275SEric Cheng 	mutex_exit(&rx_ring->rx_lock);
7678275SEric Cheng 
7688275SEric Cheng 	return (mp);
7698275SEric Cheng }
770