xref: /illumos-gate/usr/src/uts/common/io/ena/ena_rx.c (revision c46e4de3)
16f443ebcSRyan Zezeski /*
26f443ebcSRyan Zezeski  * This file and its contents are supplied under the terms of the
36f443ebcSRyan Zezeski  * Common Development and Distribution License ("CDDL"), version 1.0.
46f443ebcSRyan Zezeski  * You may only use this file in accordance with the terms of version
56f443ebcSRyan Zezeski  * 1.0 of the CDDL.
66f443ebcSRyan Zezeski  *
76f443ebcSRyan Zezeski  * A full copy of the text of the CDDL should have accompanied this
86f443ebcSRyan Zezeski  * source.  A copy of the CDDL is also available via the Internet at
96f443ebcSRyan Zezeski  * http://www.illumos.org/license/CDDL.
106f443ebcSRyan Zezeski  */
116f443ebcSRyan Zezeski 
126f443ebcSRyan Zezeski /*
13eebd18daSAndy Fiddaman  * Copyright 2024 Oxide Computer Company
146f443ebcSRyan Zezeski  */
15*c46e4de3SAndy Fiddaman 
166f443ebcSRyan Zezeski #include "ena.h"
176f443ebcSRyan Zezeski 
186f443ebcSRyan Zezeski static void
ena_refill_rx(ena_rxq_t * rxq,uint16_t num)196f443ebcSRyan Zezeski ena_refill_rx(ena_rxq_t *rxq, uint16_t num)
206f443ebcSRyan Zezeski {
216f443ebcSRyan Zezeski 	VERIFY3P(rxq, !=, NULL);
226f443ebcSRyan Zezeski 	ASSERT(MUTEX_HELD(&rxq->er_lock));
236f443ebcSRyan Zezeski 	ASSERT3U(num, <=, rxq->er_sq_num_descs);
24*c46e4de3SAndy Fiddaman 
25*c46e4de3SAndy Fiddaman 	const uint16_t modulo_mask = rxq->er_sq_num_descs - 1;
26*c46e4de3SAndy Fiddaman 	uint16_t tail_mod = rxq->er_sq_tail_idx & modulo_mask;
276f443ebcSRyan Zezeski 
286f443ebcSRyan Zezeski 	while (num != 0) {
296f443ebcSRyan Zezeski 		enahw_rx_desc_t *desc = &rxq->er_sq_descs[tail_mod];
306f443ebcSRyan Zezeski 		ena_rx_ctrl_block_t *rcb = &rxq->er_rcbs[tail_mod];
316f443ebcSRyan Zezeski 		uint16_t phase = rxq->er_sq_phase;
326f443ebcSRyan Zezeski 
336f443ebcSRyan Zezeski 		VERIFY3U(tail_mod, <, rxq->er_sq_num_descs);
346f443ebcSRyan Zezeski 		VERIFY3P(desc, !=, NULL);
356f443ebcSRyan Zezeski 		VERIFY3P(rcb, !=, NULL);
366f443ebcSRyan Zezeski 		VERIFY3P(desc, >=, rxq->er_sq_descs);
376f443ebcSRyan Zezeski 		VERIFY3P(desc, <=,
386f443ebcSRyan Zezeski 		    (rxq->er_sq_descs + rxq->er_sq_num_descs - 1));
396f443ebcSRyan Zezeski 
406f443ebcSRyan Zezeski 		desc->erd_length = rcb->ercb_dma.edb_len;
416f443ebcSRyan Zezeski 		desc->erd_req_id = tail_mod;
426f443ebcSRyan Zezeski 		VERIFY3P(rcb->ercb_dma.edb_cookie, !=, NULL);
436f443ebcSRyan Zezeski 		ena_set_dma_addr_values(rxq->er_ena,
446f443ebcSRyan Zezeski 		    rcb->ercb_dma.edb_cookie->dmac_laddress,
456f443ebcSRyan Zezeski 		    &desc->erd_buff_addr_lo, &desc->erd_buff_addr_hi);
46eebd18daSAndy Fiddaman 
47eebd18daSAndy Fiddaman 		ENAHW_RX_DESC_CLEAR_CTRL(desc);
486f443ebcSRyan Zezeski 		ENAHW_RX_DESC_SET_PHASE(desc, phase);
496f443ebcSRyan Zezeski 		ENAHW_RX_DESC_SET_FIRST(desc);
506f443ebcSRyan Zezeski 		ENAHW_RX_DESC_SET_LAST(desc);
516f443ebcSRyan Zezeski 		ENAHW_RX_DESC_SET_COMP_REQ(desc);
526f443ebcSRyan Zezeski 		DTRACE_PROBE1(ena__refill__rx, enahw_rx_desc_t *, desc);
536f443ebcSRyan Zezeski 		rxq->er_sq_tail_idx++;
54*c46e4de3SAndy Fiddaman 		tail_mod = rxq->er_sq_tail_idx & modulo_mask;
556f443ebcSRyan Zezeski 
56*c46e4de3SAndy Fiddaman 		if (tail_mod == 0)
57eebd18daSAndy Fiddaman 			rxq->er_sq_phase ^= 1;
586f443ebcSRyan Zezeski 
596f443ebcSRyan Zezeski 		num--;
606f443ebcSRyan Zezeski 	}
616f443ebcSRyan Zezeski 
626f443ebcSRyan Zezeski 	ENA_DMA_SYNC(rxq->er_sq_dma, DDI_DMA_SYNC_FORDEV);
636f443ebcSRyan Zezeski 	ena_hw_abs_write32(rxq->er_ena, rxq->er_sq_db_addr,
646f443ebcSRyan Zezeski 	    rxq->er_sq_tail_idx);
656f443ebcSRyan Zezeski }
666f443ebcSRyan Zezeski 
676f443ebcSRyan Zezeski void
ena_free_rx_dma(ena_rxq_t * rxq)686f443ebcSRyan Zezeski ena_free_rx_dma(ena_rxq_t *rxq)
696f443ebcSRyan Zezeski {
706f443ebcSRyan Zezeski 	if (rxq->er_rcbs != NULL) {
716f443ebcSRyan Zezeski 		for (uint_t i = 0; i < rxq->er_sq_num_descs; i++) {
726f443ebcSRyan Zezeski 			ena_rx_ctrl_block_t *rcb = &rxq->er_rcbs[i];
736f443ebcSRyan Zezeski 			ena_dma_free(&rcb->ercb_dma);
746f443ebcSRyan Zezeski 		}
756f443ebcSRyan Zezeski 
766f443ebcSRyan Zezeski 		kmem_free(rxq->er_rcbs,
776f443ebcSRyan Zezeski 		    sizeof (*rxq->er_rcbs) * rxq->er_sq_num_descs);
786f443ebcSRyan Zezeski 
796f443ebcSRyan Zezeski 		rxq->er_rcbs = NULL;
806f443ebcSRyan Zezeski 	}
816f443ebcSRyan Zezeski 
826f443ebcSRyan Zezeski 	ena_dma_free(&rxq->er_cq_dma);
836f443ebcSRyan Zezeski 	rxq->er_cq_descs = NULL;
846f443ebcSRyan Zezeski 	rxq->er_cq_num_descs = 0;
856f443ebcSRyan Zezeski 
866f443ebcSRyan Zezeski 	ena_dma_free(&rxq->er_sq_dma);
876f443ebcSRyan Zezeski 	rxq->er_sq_descs = NULL;
886f443ebcSRyan Zezeski 	rxq->er_sq_num_descs = 0;
896f443ebcSRyan Zezeski 
906f443ebcSRyan Zezeski 	rxq->er_state &= ~ENA_RXQ_STATE_HOST_ALLOC;
916f443ebcSRyan Zezeski }
926f443ebcSRyan Zezeski 
936f443ebcSRyan Zezeski static int
ena_alloc_rx_dma(ena_rxq_t * rxq)946f443ebcSRyan Zezeski ena_alloc_rx_dma(ena_rxq_t *rxq)
956f443ebcSRyan Zezeski {
966f443ebcSRyan Zezeski 	ena_t *ena = rxq->er_ena;
976f443ebcSRyan Zezeski 	size_t cq_descs_sz;
986f443ebcSRyan Zezeski 	size_t sq_descs_sz;
996f443ebcSRyan Zezeski 	int err = 0;
1006f443ebcSRyan Zezeski 
1016f443ebcSRyan Zezeski 	cq_descs_sz = rxq->er_cq_num_descs * sizeof (*rxq->er_cq_descs);
1026f443ebcSRyan Zezeski 	sq_descs_sz = rxq->er_sq_num_descs * sizeof (*rxq->er_sq_descs);
103*c46e4de3SAndy Fiddaman 
104*c46e4de3SAndy Fiddaman 	ena_dma_conf_t sq_conf = {
1056f443ebcSRyan Zezeski 		.edc_size = sq_descs_sz,
1066f443ebcSRyan Zezeski 		.edc_align = ENAHW_IO_SQ_DESC_BUF_ALIGNMENT,
1076f443ebcSRyan Zezeski 		.edc_sgl = 1,
1086f443ebcSRyan Zezeski 		.edc_endian = DDI_NEVERSWAP_ACC,
109*c46e4de3SAndy Fiddaman 		.edc_stream = false,
1106f443ebcSRyan Zezeski 	};
1116f443ebcSRyan Zezeski 
112*c46e4de3SAndy Fiddaman 	if (!ena_dma_alloc(ena, &rxq->er_sq_dma, &sq_conf, sq_descs_sz)) {
1136f443ebcSRyan Zezeski 		return (ENOMEM);
1146f443ebcSRyan Zezeski 	}
1156f443ebcSRyan Zezeski 
1166f443ebcSRyan Zezeski 	rxq->er_sq_descs = (void *)rxq->er_sq_dma.edb_va;
1176f443ebcSRyan Zezeski 	rxq->er_rcbs = kmem_zalloc(sizeof (*rxq->er_rcbs) *
1186f443ebcSRyan Zezeski 	    rxq->er_sq_num_descs, KM_SLEEP);
1196f443ebcSRyan Zezeski 
1206f443ebcSRyan Zezeski 	for (uint_t i = 0; i < rxq->er_sq_num_descs; i++) {
1216f443ebcSRyan Zezeski 		ena_rx_ctrl_block_t *rcb = &rxq->er_rcbs[i];
1226f443ebcSRyan Zezeski 		ena_dma_conf_t buf_conf = {
1236f443ebcSRyan Zezeski 			.edc_size = ena->ena_rx_buf_sz,
1246f443ebcSRyan Zezeski 			.edc_align = 1,
1256f443ebcSRyan Zezeski 			.edc_sgl = ena->ena_rx_sgl_max_sz,
1266f443ebcSRyan Zezeski 			.edc_endian = DDI_NEVERSWAP_ACC,
127*c46e4de3SAndy Fiddaman 			.edc_stream = true,
1286f443ebcSRyan Zezeski 		};
1296f443ebcSRyan Zezeski 
1306f443ebcSRyan Zezeski 		if (!ena_dma_alloc(ena, &rcb->ercb_dma, &buf_conf,
1316f443ebcSRyan Zezeski 		    ena->ena_rx_buf_sz)) {
1326f443ebcSRyan Zezeski 			err = ENOMEM;
1336f443ebcSRyan Zezeski 			goto error;
1346f443ebcSRyan Zezeski 		}
1356f443ebcSRyan Zezeski 	}
1366f443ebcSRyan Zezeski 
137*c46e4de3SAndy Fiddaman 	ena_dma_conf_t cq_conf = {
1386f443ebcSRyan Zezeski 		.edc_size = cq_descs_sz,
1396f443ebcSRyan Zezeski 		.edc_align = ENAHW_IO_CQ_DESC_BUF_ALIGNMENT,
1406f443ebcSRyan Zezeski 		.edc_sgl = 1,
1416f443ebcSRyan Zezeski 		.edc_endian = DDI_NEVERSWAP_ACC,
142*c46e4de3SAndy Fiddaman 		.edc_stream = false,
1436f443ebcSRyan Zezeski 	};
1446f443ebcSRyan Zezeski 
145*c46e4de3SAndy Fiddaman 	if (!ena_dma_alloc(ena, &rxq->er_cq_dma, &cq_conf, cq_descs_sz)) {
1466f443ebcSRyan Zezeski 		err = ENOMEM;
1476f443ebcSRyan Zezeski 		goto error;
1486f443ebcSRyan Zezeski 	}
1496f443ebcSRyan Zezeski 
1506f443ebcSRyan Zezeski 	rxq->er_cq_descs = (void *)rxq->er_cq_dma.edb_va;
1516f443ebcSRyan Zezeski 	rxq->er_state |= ENA_RXQ_STATE_HOST_ALLOC;
1526f443ebcSRyan Zezeski 	return (0);
1536f443ebcSRyan Zezeski 
1546f443ebcSRyan Zezeski error:
1556f443ebcSRyan Zezeski 	ena_free_rx_dma(rxq);
1566f443ebcSRyan Zezeski 	return (err);
1576f443ebcSRyan Zezeski }
1586f443ebcSRyan Zezeski 
159*c46e4de3SAndy Fiddaman bool
ena_alloc_rxq(ena_rxq_t * rxq)1606f443ebcSRyan Zezeski ena_alloc_rxq(ena_rxq_t *rxq)
1616f443ebcSRyan Zezeski {
1626f443ebcSRyan Zezeski 	int ret = 0;
1636f443ebcSRyan Zezeski 	ena_t *ena = rxq->er_ena;
1646f443ebcSRyan Zezeski 	uint16_t cq_hw_idx, sq_hw_idx;
16513776732SAndy Fiddaman 	uint32_t *cq_unmask_addr, *cq_numanode;
1666f443ebcSRyan Zezeski 	uint32_t *sq_db_addr;
1676f443ebcSRyan Zezeski 
1686f443ebcSRyan Zezeski 	/*
1696f443ebcSRyan Zezeski 	 * First, allocate the Rx data buffers.
1706f443ebcSRyan Zezeski 	 */
1716f443ebcSRyan Zezeski 	if ((ret = ena_alloc_rx_dma(rxq)) != 0) {
1726f443ebcSRyan Zezeski 		ena_err(ena, "failed to allocate Rx queue %u data buffers: %d",
1736f443ebcSRyan Zezeski 		    rxq->er_rxqs_idx, ret);
174*c46e4de3SAndy Fiddaman 		return (false);
1756f443ebcSRyan Zezeski 	}
1766f443ebcSRyan Zezeski 
1776f443ebcSRyan Zezeski 	ASSERT(rxq->er_state & ENA_RXQ_STATE_HOST_ALLOC);
1786f443ebcSRyan Zezeski 
1796f443ebcSRyan Zezeski 	/*
1806f443ebcSRyan Zezeski 	 * Second, create the Completion Queue.
1816f443ebcSRyan Zezeski 	 */
1826f443ebcSRyan Zezeski 	ret = ena_create_cq(ena,  rxq->er_cq_num_descs,
183*c46e4de3SAndy Fiddaman 	    rxq->er_cq_dma.edb_cookie->dmac_laddress, false,
18413776732SAndy Fiddaman 	    rxq->er_intr_vector, &cq_hw_idx, &cq_unmask_addr, &cq_numanode);
1856f443ebcSRyan Zezeski 
1866f443ebcSRyan Zezeski 	if (ret != 0) {
1876f443ebcSRyan Zezeski 		ena_err(ena, "failed to create Rx CQ %u: %d", rxq->er_rxqs_idx,
1886f443ebcSRyan Zezeski 		    ret);
189*c46e4de3SAndy Fiddaman 		return (false);
1906f443ebcSRyan Zezeski 	}
1916f443ebcSRyan Zezeski 
1926f443ebcSRyan Zezeski 	/* The phase must always start on 1. */
1936f443ebcSRyan Zezeski 	rxq->er_cq_phase = 1;
1946f443ebcSRyan Zezeski 	rxq->er_cq_head_idx = 0;
1956f443ebcSRyan Zezeski 	rxq->er_cq_hw_idx = cq_hw_idx;
1966f443ebcSRyan Zezeski 	rxq->er_cq_unmask_addr = cq_unmask_addr;
1976f443ebcSRyan Zezeski 	rxq->er_cq_numa_addr = cq_numanode;
1986f443ebcSRyan Zezeski 	rxq->er_state |= ENA_RXQ_STATE_CQ_CREATED;
1996f443ebcSRyan Zezeski 
2006f443ebcSRyan Zezeski 	/*
2016f443ebcSRyan Zezeski 	 * Third, create the Submission Queue to match with the above
2026f443ebcSRyan Zezeski 	 * CQ. At this time we force the SQ and CQ to have the same
2036f443ebcSRyan Zezeski 	 * number of descriptors as we only use a 1:1 completion
2046f443ebcSRyan Zezeski 	 * policy. However, in the future, we could loosen this and
2056f443ebcSRyan Zezeski 	 * use an on-demand completion policy and the two could have a
2066f443ebcSRyan Zezeski 	 * different number of descriptors.
2076f443ebcSRyan Zezeski 	 */
2086f443ebcSRyan Zezeski 	ASSERT3U(rxq->er_sq_num_descs, ==, rxq->er_cq_num_descs);
2096f443ebcSRyan Zezeski 	ret = ena_create_sq(ena, rxq->er_sq_num_descs,
210*c46e4de3SAndy Fiddaman 	    rxq->er_sq_dma.edb_cookie->dmac_laddress, false, cq_hw_idx,
2116f443ebcSRyan Zezeski 	    &sq_hw_idx, &sq_db_addr);
2126f443ebcSRyan Zezeski 
2136f443ebcSRyan Zezeski 	if (ret != 0) {
2146f443ebcSRyan Zezeski 		ena_err(ena, "failed to create Rx SQ %u: %d", rxq->er_rxqs_idx,
2156f443ebcSRyan Zezeski 		    ret);
216*c46e4de3SAndy Fiddaman 		return (false);
2176f443ebcSRyan Zezeski 	}
2186f443ebcSRyan Zezeski 
2196f443ebcSRyan Zezeski 	ASSERT3P(sq_db_addr, !=, NULL);
2206f443ebcSRyan Zezeski 	rxq->er_sq_hw_idx = sq_hw_idx;
2216f443ebcSRyan Zezeski 	rxq->er_sq_db_addr = sq_db_addr;
2226f443ebcSRyan Zezeski 	/* The phase must always start on 1. */
2236f443ebcSRyan Zezeski 	rxq->er_sq_phase = 1;
2246f443ebcSRyan Zezeski 	rxq->er_sq_tail_idx = 0;
2256f443ebcSRyan Zezeski 	rxq->er_sq_avail_descs = rxq->er_sq_num_descs;
2266f443ebcSRyan Zezeski 	rxq->er_mode = ENA_RXQ_MODE_INTR;
2276f443ebcSRyan Zezeski 	rxq->er_state |= ENA_RXQ_STATE_SQ_CREATED;
2286f443ebcSRyan Zezeski 
229*c46e4de3SAndy Fiddaman 	return (true);
2306f443ebcSRyan Zezeski }
2316f443ebcSRyan Zezeski 
2326f443ebcSRyan Zezeski void
ena_cleanup_rxq(ena_rxq_t * rxq,bool resetting)233*c46e4de3SAndy Fiddaman ena_cleanup_rxq(ena_rxq_t *rxq, bool resetting)
2346f443ebcSRyan Zezeski {
2356f443ebcSRyan Zezeski 	int ret = 0;
2366f443ebcSRyan Zezeski 	ena_t *ena = rxq->er_ena;
2376f443ebcSRyan Zezeski 
2386f443ebcSRyan Zezeski 	if ((rxq->er_state & ENA_RXQ_STATE_SQ_CREATED) != 0) {
239*c46e4de3SAndy Fiddaman 		if (!resetting) {
240*c46e4de3SAndy Fiddaman 			ret = ena_destroy_sq(ena, rxq->er_sq_hw_idx, false);
2416f443ebcSRyan Zezeski 
242*c46e4de3SAndy Fiddaman 			if (ret != 0) {
243*c46e4de3SAndy Fiddaman 				ena_err(ena, "failed to destroy Rx SQ %u: %d",
244*c46e4de3SAndy Fiddaman 				    rxq->er_rxqs_idx, ret);
245*c46e4de3SAndy Fiddaman 			}
2466f443ebcSRyan Zezeski 		}
2476f443ebcSRyan Zezeski 
2486f443ebcSRyan Zezeski 		rxq->er_sq_hw_idx = 0;
2496f443ebcSRyan Zezeski 		rxq->er_sq_db_addr = NULL;
2506f443ebcSRyan Zezeski 		rxq->er_sq_tail_idx = 0;
2516f443ebcSRyan Zezeski 		rxq->er_sq_phase = 0;
2526f443ebcSRyan Zezeski 		rxq->er_state &= ~ENA_RXQ_STATE_SQ_CREATED;
253eebd18daSAndy Fiddaman 		rxq->er_state &= ~ENA_RXQ_STATE_SQ_FILLED;
2546f443ebcSRyan Zezeski 	}
2556f443ebcSRyan Zezeski 
2566f443ebcSRyan Zezeski 	if ((rxq->er_state & ENA_RXQ_STATE_CQ_CREATED) != 0) {
257*c46e4de3SAndy Fiddaman 		if (!resetting) {
258*c46e4de3SAndy Fiddaman 			ret = ena_destroy_cq(ena, rxq->er_cq_hw_idx);
2596f443ebcSRyan Zezeski 
260*c46e4de3SAndy Fiddaman 			if (ret != 0) {
261*c46e4de3SAndy Fiddaman 				ena_err(ena, "failed to destroy Rx CQ %u: %d",
262*c46e4de3SAndy Fiddaman 				    rxq->er_rxqs_idx, ret);
263*c46e4de3SAndy Fiddaman 			}
2646f443ebcSRyan Zezeski 		}
2656f443ebcSRyan Zezeski 
2666f443ebcSRyan Zezeski 		rxq->er_cq_hw_idx = 0;
2676f443ebcSRyan Zezeski 		rxq->er_cq_head_idx = 0;
2686f443ebcSRyan Zezeski 		rxq->er_cq_phase = 0;
2696f443ebcSRyan Zezeski 		rxq->er_cq_unmask_addr = NULL;
2706f443ebcSRyan Zezeski 		rxq->er_cq_numa_addr = NULL;
2716f443ebcSRyan Zezeski 		rxq->er_state &= ~ENA_RXQ_STATE_CQ_CREATED;
2726f443ebcSRyan Zezeski 	}
2736f443ebcSRyan Zezeski 
2746f443ebcSRyan Zezeski 	ena_free_rx_dma(rxq);
2756f443ebcSRyan Zezeski 	ASSERT3S(rxq->er_state, ==, ENA_RXQ_STATE_NONE);
2766f443ebcSRyan Zezeski }
2776f443ebcSRyan Zezeski 
2786f443ebcSRyan Zezeski void
ena_ring_rx_stop(mac_ring_driver_t rh)2796f443ebcSRyan Zezeski ena_ring_rx_stop(mac_ring_driver_t rh)
2806f443ebcSRyan Zezeski {
2816f443ebcSRyan Zezeski 	ena_rxq_t *rxq = (ena_rxq_t *)rh;
2826f443ebcSRyan Zezeski 	uint32_t intr_ctrl;
2836f443ebcSRyan Zezeski 
2846f443ebcSRyan Zezeski 	intr_ctrl = ena_hw_abs_read32(rxq->er_ena, rxq->er_cq_unmask_addr);
2856f443ebcSRyan Zezeski 	ENAHW_REG_INTR_MASK(intr_ctrl);
2866f443ebcSRyan Zezeski 	ena_hw_abs_write32(rxq->er_ena, rxq->er_cq_unmask_addr, intr_ctrl);
2876f443ebcSRyan Zezeski 
2886f443ebcSRyan Zezeski 	rxq->er_state &= ~ENA_RXQ_STATE_RUNNING;
2896f443ebcSRyan Zezeski 	rxq->er_state &= ~ENA_RXQ_STATE_READY;
2906f443ebcSRyan Zezeski }
2916f443ebcSRyan Zezeski 
2926f443ebcSRyan Zezeski int
ena_ring_rx_start(mac_ring_driver_t rh,uint64_t gen_num)2936f443ebcSRyan Zezeski ena_ring_rx_start(mac_ring_driver_t rh, uint64_t gen_num)
2946f443ebcSRyan Zezeski {
2956f443ebcSRyan Zezeski 	ena_rxq_t *rxq = (ena_rxq_t *)rh;
2966f443ebcSRyan Zezeski 	ena_t *ena = rxq->er_ena;
2976f443ebcSRyan Zezeski 	uint32_t intr_ctrl;
2986f443ebcSRyan Zezeski 
299*c46e4de3SAndy Fiddaman 	ena_dbg(ena, "ring_rx_start %p: state 0x%x", rxq, rxq->er_state);
300eebd18daSAndy Fiddaman 
3016f443ebcSRyan Zezeski 	mutex_enter(&rxq->er_lock);
302eebd18daSAndy Fiddaman 	if ((rxq->er_state & ENA_RXQ_STATE_SQ_FILLED) == 0) {
303eebd18daSAndy Fiddaman 		/*
304eebd18daSAndy Fiddaman 		 * The ENA controller gets upset and sets the fatal error bit
305eebd18daSAndy Fiddaman 		 * in its status register if we write a value to an RX SQ's
306eebd18daSAndy Fiddaman 		 * doorbell that is past its current head. This makes sense as
307eebd18daSAndy Fiddaman 		 * it would represent there being more descriptors available
308eebd18daSAndy Fiddaman 		 * than can fit in the ring. For this reason, we make sure that
309eebd18daSAndy Fiddaman 		 * we only fill the ring once, even if it is started multiple
310eebd18daSAndy Fiddaman 		 * times.
311eebd18daSAndy Fiddaman 		 * The `- 1` below is harder to explain. If we completely fill
312eebd18daSAndy Fiddaman 		 * the SQ ring, then at some time later that seems to be
313eebd18daSAndy Fiddaman 		 * independent of how many times we've been around the ring,
314eebd18daSAndy Fiddaman 		 * the ENA controller will set the fatal error bit and stop
315eebd18daSAndy Fiddaman 		 * responding. Leaving a gap prevents this somehow and it is
316eebd18daSAndy Fiddaman 		 * what the other open source drivers do.
317eebd18daSAndy Fiddaman 		 */
318eebd18daSAndy Fiddaman 		ena_refill_rx(rxq, rxq->er_sq_num_descs - 1);
319eebd18daSAndy Fiddaman 		rxq->er_state |= ENA_RXQ_STATE_SQ_FILLED;
320eebd18daSAndy Fiddaman 	}
3216f443ebcSRyan Zezeski 	rxq->er_m_gen_num = gen_num;
3226f443ebcSRyan Zezeski 	rxq->er_intr_limit = ena->ena_rxq_intr_limit;
3236f443ebcSRyan Zezeski 	mutex_exit(&rxq->er_lock);
3246f443ebcSRyan Zezeski 
3256f443ebcSRyan Zezeski 	rxq->er_state |= ENA_RXQ_STATE_READY;
3266f443ebcSRyan Zezeski 
3276f443ebcSRyan Zezeski 	intr_ctrl = ena_hw_abs_read32(ena, rxq->er_cq_unmask_addr);
3286f443ebcSRyan Zezeski 	ENAHW_REG_INTR_UNMASK(intr_ctrl);
3296f443ebcSRyan Zezeski 	ena_hw_abs_write32(ena, rxq->er_cq_unmask_addr, intr_ctrl);
3306f443ebcSRyan Zezeski 	rxq->er_state |= ENA_RXQ_STATE_RUNNING;
3316f443ebcSRyan Zezeski 	return (0);
3326f443ebcSRyan Zezeski }
3336f443ebcSRyan Zezeski 
3346f443ebcSRyan Zezeski mblk_t *
ena_ring_rx(ena_rxq_t * rxq,int poll_bytes)3356f443ebcSRyan Zezeski ena_ring_rx(ena_rxq_t *rxq, int poll_bytes)
3366f443ebcSRyan Zezeski {
3376f443ebcSRyan Zezeski 	ena_t *ena = rxq->er_ena;
338*c46e4de3SAndy Fiddaman 	const uint16_t modulo_mask = rxq->er_cq_num_descs - 1;
339*c46e4de3SAndy Fiddaman 	uint16_t head_mod = rxq->er_cq_head_idx & modulo_mask;
3406f443ebcSRyan Zezeski 	uint64_t total_bytes = 0;
3416f443ebcSRyan Zezeski 	uint64_t num_frames = 0;
3426f443ebcSRyan Zezeski 	enahw_rx_cdesc_t *cdesc;
343*c46e4de3SAndy Fiddaman 	bool polling = true;
3446f443ebcSRyan Zezeski 	mblk_t *head = NULL;
3456f443ebcSRyan Zezeski 	mblk_t *tail = NULL;
3466f443ebcSRyan Zezeski 
3476f443ebcSRyan Zezeski 	ASSERT(MUTEX_HELD(&rxq->er_lock));
3486f443ebcSRyan Zezeski 	ENA_DMA_SYNC(rxq->er_cq_dma, DDI_DMA_SYNC_FORKERNEL);
3496f443ebcSRyan Zezeski 
3506f443ebcSRyan Zezeski 	if (poll_bytes == ENA_INTERRUPT_MODE) {
351*c46e4de3SAndy Fiddaman 		polling = false;
3526f443ebcSRyan Zezeski 	}
3536f443ebcSRyan Zezeski 
3546f443ebcSRyan Zezeski 	cdesc = &rxq->er_cq_descs[head_mod];
3556f443ebcSRyan Zezeski 	VERIFY3P(cdesc, >=, rxq->er_cq_descs);
3566f443ebcSRyan Zezeski 	VERIFY3P(cdesc, <=, (rxq->er_cq_descs + rxq->er_cq_num_descs - 1));
3576f443ebcSRyan Zezeski 
3586f443ebcSRyan Zezeski 	while (ENAHW_RX_CDESC_PHASE(cdesc) == rxq->er_cq_phase) {
359*c46e4de3SAndy Fiddaman 		bool first, last;
3606f443ebcSRyan Zezeski 		ena_rx_ctrl_block_t *rcb;
3616f443ebcSRyan Zezeski 		uint16_t req_id;
3626f443ebcSRyan Zezeski 		mblk_t *mp;
3636f443ebcSRyan Zezeski 		enahw_io_l3_proto_t l3proto;
3646f443ebcSRyan Zezeski 		enahw_io_l4_proto_t l4proto;
365*c46e4de3SAndy Fiddaman 		bool l4csum_checked;
3666f443ebcSRyan Zezeski 		uint32_t hflags = 0;
3676f443ebcSRyan Zezeski 
3686f443ebcSRyan Zezeski 		VERIFY3U(head_mod, <, rxq->er_cq_num_descs);
3696f443ebcSRyan Zezeski 		/*
3706f443ebcSRyan Zezeski 		 * Currently, all incoming frames fit in a single Rx
3716f443ebcSRyan Zezeski 		 * buffer (erd_length > total frame size). In the
3726f443ebcSRyan Zezeski 		 * future, if we decide to loan buffers which are
3736f443ebcSRyan Zezeski 		 * smaller, we will need to modify this code to read
3746f443ebcSRyan Zezeski 		 * one or more descriptors (based on frame size).
3756f443ebcSRyan Zezeski 		 *
3766f443ebcSRyan Zezeski 		 * For this reason we do not expect any frame to span
3776f443ebcSRyan Zezeski 		 * multiple descriptors. Therefore, we drop any data
3786f443ebcSRyan Zezeski 		 * not delivered as a single descriptor, i.e., where
3796f443ebcSRyan Zezeski 		 * 'first' and 'last' are both true.
3806f443ebcSRyan Zezeski 		 */
3816f443ebcSRyan Zezeski 		first = ENAHW_RX_CDESC_FIRST(cdesc);
3826f443ebcSRyan Zezeski 		last = ENAHW_RX_CDESC_LAST(cdesc);
3836f443ebcSRyan Zezeski 
3846f443ebcSRyan Zezeski 		if (!first || !last) {
3856f443ebcSRyan Zezeski 			mutex_enter(&rxq->er_stat_lock);
3866f443ebcSRyan Zezeski 			rxq->er_stat.ers_multi_desc.value.ui64++;
3876f443ebcSRyan Zezeski 			mutex_exit(&rxq->er_stat_lock);
3886f443ebcSRyan Zezeski 			goto next_desc;
3896f443ebcSRyan Zezeski 		}
3906f443ebcSRyan Zezeski 
3916f443ebcSRyan Zezeski 		req_id = cdesc->erc_req_id;
3926f443ebcSRyan Zezeski 		VERIFY3U(req_id, <, rxq->er_cq_num_descs);
3936f443ebcSRyan Zezeski 		rcb = &rxq->er_rcbs[req_id];
3946f443ebcSRyan Zezeski 		rcb->ercb_offset = cdesc->erc_offset;
3956f443ebcSRyan Zezeski 		rcb->ercb_length = cdesc->erc_length;
3966f443ebcSRyan Zezeski 		ASSERT3U(rcb->ercb_length, <=, ena->ena_max_frame_total);
3976f443ebcSRyan Zezeski 		mp = allocb(rcb->ercb_length + ENA_RX_BUF_IPHDR_ALIGNMENT, 0);
3986f443ebcSRyan Zezeski 
3996f443ebcSRyan Zezeski 		/*
4006f443ebcSRyan Zezeski 		 * If we can't allocate an mblk, things are looking
4016f443ebcSRyan Zezeski 		 * grim. Forget about this frame and move on.
4026f443ebcSRyan Zezeski 		 */
4036f443ebcSRyan Zezeski 		if (mp == NULL) {
4046f443ebcSRyan Zezeski 			mutex_enter(&rxq->er_stat_lock);
4056f443ebcSRyan Zezeski 			rxq->er_stat.ers_allocb_fail.value.ui64++;
4066f443ebcSRyan Zezeski 			mutex_exit(&rxq->er_stat_lock);
4076f443ebcSRyan Zezeski 			goto next_desc;
4086f443ebcSRyan Zezeski 		}
4096f443ebcSRyan Zezeski 
4106f443ebcSRyan Zezeski 		/*
4116f443ebcSRyan Zezeski 		 * As we pull frames we need to link them together as
4126f443ebcSRyan Zezeski 		 * one chain to be delivered up to mac.
4136f443ebcSRyan Zezeski 		 */
4146f443ebcSRyan Zezeski 		if (head == NULL) {
4156f443ebcSRyan Zezeski 			head = mp;
4166f443ebcSRyan Zezeski 		} else {
4176f443ebcSRyan Zezeski 			tail->b_next = mp;
4186f443ebcSRyan Zezeski 		}
4196f443ebcSRyan Zezeski 
4206f443ebcSRyan Zezeski 		tail = mp;
4216f443ebcSRyan Zezeski 
4226f443ebcSRyan Zezeski 		/*
4236f443ebcSRyan Zezeski 		 * We need to make sure the bytes are copied to the
4246f443ebcSRyan Zezeski 		 * correct offset to achieve 4-byte IP header
4256f443ebcSRyan Zezeski 		 * alignment.
4266f443ebcSRyan Zezeski 		 *
4276f443ebcSRyan Zezeski 		 * If we start using desballoc on the buffers, then we
4286f443ebcSRyan Zezeski 		 * will need to make sure to apply this offset to the
4296f443ebcSRyan Zezeski 		 * DMA buffers as well. Though it may be the case the
4306f443ebcSRyan Zezeski 		 * device does this implicitly and that's what
4316f443ebcSRyan Zezeski 		 * cdesc->erc_offset is for; we don't know because
4326f443ebcSRyan Zezeski 		 * it's not documented.
4336f443ebcSRyan Zezeski 		 */
4346f443ebcSRyan Zezeski 		mp->b_wptr += ENA_RX_BUF_IPHDR_ALIGNMENT;
4356f443ebcSRyan Zezeski 		mp->b_rptr += ENA_RX_BUF_IPHDR_ALIGNMENT;
4366f443ebcSRyan Zezeski 		bcopy(rcb->ercb_dma.edb_va + rcb->ercb_offset, mp->b_wptr,
4376f443ebcSRyan Zezeski 		    rcb->ercb_length);
4386f443ebcSRyan Zezeski 		mp->b_wptr += rcb->ercb_length;
4396f443ebcSRyan Zezeski 		total_bytes += rcb->ercb_length;
4406f443ebcSRyan Zezeski 		VERIFY3P(mp->b_wptr, >, mp->b_rptr);
4416f443ebcSRyan Zezeski 		VERIFY3P(mp->b_wptr, <=, mp->b_datap->db_lim);
4426f443ebcSRyan Zezeski 
4436f443ebcSRyan Zezeski 		l3proto = ENAHW_RX_CDESC_L3_PROTO(cdesc);
4446f443ebcSRyan Zezeski 		l4proto = ENAHW_RX_CDESC_L4_PROTO(cdesc);
4456f443ebcSRyan Zezeski 
4466f443ebcSRyan Zezeski 		/*
4476f443ebcSRyan Zezeski 		 * When it comes to bad TCP/IP checksums we do not
4486f443ebcSRyan Zezeski 		 * discard the packet at this level. Instead, we let
4496f443ebcSRyan Zezeski 		 * it percolate up for further processing and tracking
4506f443ebcSRyan Zezeski 		 * by the upstream TCP/IP stack.
4516f443ebcSRyan Zezeski 		 */
4526f443ebcSRyan Zezeski 		if (ena->ena_rx_l3_ipv4_csum &&
4536f443ebcSRyan Zezeski 		    l3proto == ENAHW_IO_L3_PROTO_IPV4) {
454*c46e4de3SAndy Fiddaman 			bool l3_csum_err =
4556f443ebcSRyan Zezeski 			    ENAHW_RX_CDESC_L3_CSUM_ERR(cdesc);
4566f443ebcSRyan Zezeski 
4576f443ebcSRyan Zezeski 			if (l3_csum_err) {
4586f443ebcSRyan Zezeski 				mutex_enter(&rxq->er_stat_lock);
4596f443ebcSRyan Zezeski 				rxq->er_stat.ers_hck_ipv4_err.value.ui64++;
4606f443ebcSRyan Zezeski 				mutex_exit(&rxq->er_stat_lock);
4616f443ebcSRyan Zezeski 			} else {
4626f443ebcSRyan Zezeski 				hflags |= HCK_IPV4_HDRCKSUM_OK;
4636f443ebcSRyan Zezeski 			}
4646f443ebcSRyan Zezeski 		}
4656f443ebcSRyan Zezeski 
4666f443ebcSRyan Zezeski 		l4csum_checked = ENAHW_RX_CDESC_L4_CSUM_CHECKED(cdesc);
4676f443ebcSRyan Zezeski 
4686f443ebcSRyan Zezeski 		if (ena->ena_rx_l4_ipv4_csum && l4csum_checked &&
4696f443ebcSRyan Zezeski 		    l4proto == ENAHW_IO_L4_PROTO_TCP) {
470*c46e4de3SAndy Fiddaman 			bool l4_csum_err =
4716f443ebcSRyan Zezeski 			    ENAHW_RX_CDESC_L4_CSUM_ERR(cdesc);
4726f443ebcSRyan Zezeski 
4736f443ebcSRyan Zezeski 			if (l4_csum_err) {
4746f443ebcSRyan Zezeski 				mutex_enter(&rxq->er_stat_lock);
4756f443ebcSRyan Zezeski 				rxq->er_stat.ers_hck_l4_err.value.ui64++;
4766f443ebcSRyan Zezeski 				mutex_exit(&rxq->er_stat_lock);
4776f443ebcSRyan Zezeski 			} else {
4786f443ebcSRyan Zezeski 				hflags |= HCK_FULLCKSUM_OK;
4796f443ebcSRyan Zezeski 			}
4806f443ebcSRyan Zezeski 		}
4816f443ebcSRyan Zezeski 
4826f443ebcSRyan Zezeski 		if (hflags != 0) {
4836f443ebcSRyan Zezeski 			mac_hcksum_set(mp, 0, 0, 0, 0, hflags);
4846f443ebcSRyan Zezeski 		}
4856f443ebcSRyan Zezeski 
4866f443ebcSRyan Zezeski next_desc:
4876f443ebcSRyan Zezeski 		/*
4886f443ebcSRyan Zezeski 		 * Technically, if we arrived here due to a failure,
4896f443ebcSRyan Zezeski 		 * then we did not read a new frame. However, we count
4906f443ebcSRyan Zezeski 		 * it all the same anyways in order to count it as
4916f443ebcSRyan Zezeski 		 * progress to the interrupt work limit. The failure
4926f443ebcSRyan Zezeski 		 * stats will allow us to differentiate good frames
4936f443ebcSRyan Zezeski 		 * from bad.
4946f443ebcSRyan Zezeski 		 */
4956f443ebcSRyan Zezeski 		num_frames++;
4966f443ebcSRyan Zezeski 		rxq->er_cq_head_idx++;
497*c46e4de3SAndy Fiddaman 		head_mod = rxq->er_cq_head_idx & modulo_mask;
498*c46e4de3SAndy Fiddaman 		if (head_mod == 0)
499eebd18daSAndy Fiddaman 			rxq->er_cq_phase ^= 1;
5006f443ebcSRyan Zezeski 
501*c46e4de3SAndy Fiddaman 		if (polling && total_bytes > poll_bytes) {
5026f443ebcSRyan Zezeski 			break;
503*c46e4de3SAndy Fiddaman 		} else if (!polling && num_frames >= rxq->er_intr_limit) {
5046f443ebcSRyan Zezeski 			mutex_enter(&rxq->er_stat_lock);
5056f443ebcSRyan Zezeski 			rxq->er_stat.ers_intr_limit.value.ui64++;
5066f443ebcSRyan Zezeski 			mutex_exit(&rxq->er_stat_lock);
5076f443ebcSRyan Zezeski 			break;
5086f443ebcSRyan Zezeski 		}
5096f443ebcSRyan Zezeski 
5106f443ebcSRyan Zezeski 		cdesc = &rxq->er_cq_descs[head_mod];
5116f443ebcSRyan Zezeski 		VERIFY3P(cdesc, >=, rxq->er_cq_descs);
5126f443ebcSRyan Zezeski 		VERIFY3P(cdesc, <=,
5136f443ebcSRyan Zezeski 		    (rxq->er_cq_descs + rxq->er_cq_num_descs - 1));
5146f443ebcSRyan Zezeski 	}
5156f443ebcSRyan Zezeski 
516eebd18daSAndy Fiddaman 	if (num_frames > 0) {
517eebd18daSAndy Fiddaman 		mutex_enter(&rxq->er_stat_lock);
518eebd18daSAndy Fiddaman 		rxq->er_stat.ers_packets.value.ui64 += num_frames;
519eebd18daSAndy Fiddaman 		rxq->er_stat.ers_bytes.value.ui64 += total_bytes;
520eebd18daSAndy Fiddaman 		mutex_exit(&rxq->er_stat_lock);
521eebd18daSAndy Fiddaman 
522*c46e4de3SAndy Fiddaman 		DTRACE_PROBE5(rx__frames, ena_rxq_t *, rxq, mblk_t *, head,
523*c46e4de3SAndy Fiddaman 		    bool, polling, uint64_t, num_frames, uint64_t, total_bytes);
524eebd18daSAndy Fiddaman 		ena_refill_rx(rxq, num_frames);
525eebd18daSAndy Fiddaman 	}
5266f443ebcSRyan Zezeski 
5276f443ebcSRyan Zezeski 	return (head);
5286f443ebcSRyan Zezeski }
5296f443ebcSRyan Zezeski 
5306f443ebcSRyan Zezeski void
ena_rx_intr_work(ena_rxq_t * rxq)5316f443ebcSRyan Zezeski ena_rx_intr_work(ena_rxq_t *rxq)
5326f443ebcSRyan Zezeski {
5336f443ebcSRyan Zezeski 	mblk_t *mp;
5346f443ebcSRyan Zezeski 
5356f443ebcSRyan Zezeski 	mutex_enter(&rxq->er_lock);
5366f443ebcSRyan Zezeski 	mp = ena_ring_rx(rxq, ENA_INTERRUPT_MODE);
5376f443ebcSRyan Zezeski 	mutex_exit(&rxq->er_lock);
5386f443ebcSRyan Zezeski 
5396f443ebcSRyan Zezeski 	if (mp == NULL) {
5406f443ebcSRyan Zezeski 		return;
5416f443ebcSRyan Zezeski 	}
5426f443ebcSRyan Zezeski 
5436f443ebcSRyan Zezeski 	mac_rx_ring(rxq->er_ena->ena_mh, rxq->er_mrh, mp, rxq->er_m_gen_num);
5446f443ebcSRyan Zezeski }
5456f443ebcSRyan Zezeski 
5466f443ebcSRyan Zezeski mblk_t *
ena_ring_rx_poll(void * rh,int poll_bytes)5476f443ebcSRyan Zezeski ena_ring_rx_poll(void *rh, int poll_bytes)
5486f443ebcSRyan Zezeski {
5496f443ebcSRyan Zezeski 	ena_rxq_t *rxq = rh;
5506f443ebcSRyan Zezeski 	mblk_t *mp;
5516f443ebcSRyan Zezeski 
5526f443ebcSRyan Zezeski 	ASSERT3S(poll_bytes, >, 0);
5536f443ebcSRyan Zezeski 
5546f443ebcSRyan Zezeski 	mutex_enter(&rxq->er_lock);
5556f443ebcSRyan Zezeski 	mp = ena_ring_rx(rxq, poll_bytes);
5566f443ebcSRyan Zezeski 	mutex_exit(&rxq->er_lock);
5576f443ebcSRyan Zezeski 
5586f443ebcSRyan Zezeski 	return (mp);
5596f443ebcSRyan Zezeski }
560