コード例 #1
0
/* Enqueue a single packet 'mp' for sending */
static boolean_t
virtionet_send(virtionet_state_t *sp, mblk_t *mp)
{
	void			*buf;
	size_t			mlen;
	int			idx;

	ASSERT(mp != NULL);

	mlen = msgsize(mp);

	ASSERT(mlen <= 2048);

	cmn_err(CE_CONT, "Sending message of %d bytes\n", mlen);

	idx = sp->txq->vr_avail->idx;
	buf = sp->txbuf->addr + idx * 2048;
	mcopymsg(mp, buf);
	sp->txq->vr_desc[idx].len = mlen;

	ddi_dma_sync(sp->txbuf->hdl, idx * 2048, mlen, DDI_DMA_SYNC_FORDEV);
	sp->txq->vr_avail->idx++;
	/* The next is suboptimal, should calculate exact offset/size */
	ddi_dma_sync(sp->txq->vq_dma.hdl, 0, 0, DDI_DMA_SYNC_FORDEV);

	return (B_TRUE);
}
コード例 #2
0
ファイル: pcn.c プロジェクト: apprisi/illumos-gate
static void
pcn_resetrings(pcn_t *pcnp)
{
	int i;
	uint16_t bufsz = ((~(PCN_BUFSZ) + 1) & PCN_RXLEN_BUFSZ) | PCN_RXLEN_MBO;

	pcnp->pcn_rxhead = 0;
	pcnp->pcn_txreclaim = 0;
	pcnp->pcn_txsend = 0;
	pcnp->pcn_txavail = PCN_TXRING;

	/* reset rx descriptor values */
	for (i = 0; i < PCN_RXRING; i++) {
		pcn_rx_desc_t	*rmd = &pcnp->pcn_rxdescp[i];
		pcn_buf_t	*rxb = pcnp->pcn_rxbufs[i];

		rmd->pcn_rxlen = rmd->pcn_rsvd0 = 0;
		rmd->pcn_rbaddr = rxb->pb_paddr;
		rmd->pcn_bufsz = bufsz;
		rmd->pcn_rxstat = PCN_RXSTAT_OWN;
	}
	(void) ddi_dma_sync(pcnp->pcn_rxdesc_dmah, 0,
	    PCN_RXRING * sizeof (pcn_rx_desc_t), DDI_DMA_SYNC_FORDEV);

	/* reset tx descriptor values */
	for (i = 0; i < PCN_TXRING; i++) {
		pcn_tx_desc_t	*txd = &pcnp->pcn_txdescp[i];
		pcn_buf_t	*txb = pcnp->pcn_txbufs[i];

		txd->pcn_txstat = txd->pcn_txctl = txd->pcn_uspace = 0;
		txd->pcn_tbaddr = txb->pb_paddr;
	}
	(void) ddi_dma_sync(pcnp->pcn_txdesc_dmah, 0,
	    PCN_TXRING * sizeof (pcn_tx_desc_t), DDI_DMA_SYNC_FORDEV);

	/* set addresses of decriptors */
	pcn_csr_write(pcnp, PCN_CSR_RXADDR0, pcnp->pcn_rxdesc_paddr & 0xFFFF);
	pcn_csr_write(pcnp, PCN_CSR_RXADDR1,
	    (pcnp->pcn_rxdesc_paddr >> 16) & 0xFFFF);

	pcn_csr_write(pcnp, PCN_CSR_TXADDR0, pcnp->pcn_txdesc_paddr & 0xFFFF);
	pcn_csr_write(pcnp, PCN_CSR_TXADDR1,
	    (pcnp->pcn_txdesc_paddr >> 16) & 0xFFFF);

	/* set the ring sizes */
	pcn_csr_write(pcnp, PCN_CSR_RXRINGLEN, (~PCN_RXRING) + 1);
	pcn_csr_write(pcnp, PCN_CSR_TXRINGLEN, (~PCN_TXRING) + 1);
}
コード例 #3
0
/*
 * audioixp_sync()
 *
 * Description:
 *	This is called by the framework to synchronize DMA caches.
 *
 * Arguments:
 *	void	*arg		The DMA engine to sync
 */
static void
audioixp_sync(void *arg, unsigned nframes)
{
	audioixp_port_t *port = arg;
	_NOTE(ARGUNUSED(nframes));

	(void) ddi_dma_sync(port->samp_dmah, 0, 0, port->sync_dir);
}
コード例 #4
0
/* Check the status of the virtqueue */
static void
virtionet_check_vq(virtionet_state_t *sp, virtqueue_t *vqp)
{
	ddi_dma_sync(vqp->vq_dma.hdl, 0, 0, DDI_DMA_SYNC_FORKERNEL);
	cmn_err(CE_CONT, "Avail [flags=0x%x, idx=%d]\n",
	    vqp->vr_avail->flags, vqp->vr_avail->idx);
	cmn_err(CE_CONT, "Used  [flags=0x%x, idx=%d]\n",
	    vqp->vr_used->flags, vqp->vr_used->idx);
}
コード例 #5
0
/*ARGSUSED*/
void
ghd_tran_sync_pkt(struct scsi_address *ap, struct scsi_pkt *pktp)
{
	gcmd_t *gcmdp = PKTP2GCMDP(pktp);
	int	status;

	if (gcmdp->cmd_dma_handle) {
		status = ddi_dma_sync(gcmdp->cmd_dma_handle, 0, 0,
		    (gcmdp->cmd_dma_flags & DDI_DMA_READ) ?
		    DDI_DMA_SYNC_FORCPU : DDI_DMA_SYNC_FORDEV);
		if (status != DDI_SUCCESS) {
			cmn_err(CE_WARN, "ghd_tran_sync_pkt() fail\n");
		}
	}
}
コード例 #6
0
ファイル: oce_tx.c プロジェクト: apprisi/illumos-gate
/*
 * function to copy the packet to preallocated Tx buffer
 *
 * wq - pointer to WQ
 * wqed - Pointer to WQE descriptor
 * mp - Pointer to packet chain
 * pktlen - Size of the packet
 *
 * return 0=>success, error code otherwise
 */
static int
oce_bcopy_wqe(struct oce_wq *wq, oce_wqe_desc_t *wqed, mblk_t *mp,
    uint32_t pkt_len)
{
	oce_wq_bdesc_t *wqbd;
	caddr_t buf_va;
	struct oce_dev *dev = wq->parent;
	int len = 0;

	wqbd = oce_wqb_alloc(wq);
	if (wqbd == NULL) {
		atomic_inc_32(&dev->tx_noxmtbuf);
		oce_log(dev, CE_WARN, MOD_TX, "%s",
		    "wqb pool empty");
		return (ENOMEM);
	}

	/* create a fragment wqe for the packet */
	wqed->frag[wqed->frag_idx].u0.s.frag_pa_hi = wqbd->frag_addr.dw.addr_hi;
	wqed->frag[wqed->frag_idx].u0.s.frag_pa_lo = wqbd->frag_addr.dw.addr_lo;
	buf_va = DBUF_VA(wqbd->wqb);

	/* copy pkt into buffer */
	for (len = 0; mp != NULL && len < pkt_len; mp = mp->b_cont) {
		bcopy(mp->b_rptr, buf_va, MBLKL(mp));
		buf_va += MBLKL(mp);
		len += MBLKL(mp);
	}

	(void) ddi_dma_sync(DBUF_DHDL(wqbd->wqb), 0, pkt_len,
	    DDI_DMA_SYNC_FORDEV);

	if (oce_fm_check_dma_handle(dev, DBUF_DHDL(wqbd->wqb))) {
		ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED);
		/* Free the buffer */
		oce_wqb_free(wq, wqbd);
		return (EIO);
	}
	wqed->frag[wqed->frag_idx].u0.s.frag_len   =  pkt_len;
	wqed->hdesc[wqed->nhdl].hdl = (void *)(wqbd);
	wqed->hdesc[wqed->nhdl].type = COPY_WQE;
	wqed->frag_cnt++;
	wqed->frag_idx++;
	wqed->nhdl++;
	return (0);
} /* oce_bcopy_wqe */
コード例 #7
0
ファイル: oce_tx.c プロジェクト: apprisi/illumos-gate
static inline int
oce_process_tx_compl(struct oce_wq *wq, boolean_t rearm)
{
	struct oce_nic_tx_cqe *cqe;
	uint16_t num_cqe = 0;
	struct oce_cq *cq;
	oce_wqe_desc_t *wqed;
	int wqe_freed = 0;
	struct oce_dev *dev;

	cq  = wq->cq;
	dev = wq->parent;
	(void) ddi_dma_sync(cq->ring->dbuf->dma_handle, 0, 0,
	    DDI_DMA_SYNC_FORKERNEL);

	mutex_enter(&wq->txc_lock);
	cqe = RING_GET_CONSUMER_ITEM_VA(cq->ring, struct oce_nic_tx_cqe);
	while (WQ_CQE_VALID(cqe)) {

		DW_SWAP(u32ptr(cqe), sizeof (struct oce_nic_tx_cqe));

		/* update stats */
		if (cqe->u0.s.status != 0) {
			atomic_inc_32(&dev->tx_errors);
		}

		/* complete the WQEs */
		wqed = OCE_LIST_REM_HEAD(&wq->wqe_desc_list);

		wqe_freed = wqed->wqe_cnt;
		oce_free_wqed(wq, wqed);
		RING_GET(wq->ring, wqe_freed);
		atomic_add_32(&wq->wq_free, wqe_freed);
		/* clear the valid bit and progress cqe */
		WQ_CQE_INVALIDATE(cqe);
		RING_GET(cq->ring, 1);
		cqe = RING_GET_CONSUMER_ITEM_VA(cq->ring,
		    struct oce_nic_tx_cqe);
		num_cqe++;
	} /* for all valid CQE */
	mutex_exit(&wq->txc_lock);
	if (num_cqe)
		oce_arm_cq(wq->parent, cq->cq_id, num_cqe, rearm);
	return (num_cqe);
} /* oce_process_tx_completion */
コード例 #8
0
ファイル: dvma.c プロジェクト: andreiw/polaris
void
dvma_sync(ddi_dma_handle_t h, uint_t objindex, uint_t type)
{
	register ddi_dma_impl_t *mp = (ddi_dma_impl_t *)h;
	struct fast_dvma *nexus_private;
	struct dvma_ops *nexus_funcptr;

	if (mp->dmai_rflags & DMP_BYPASSNEXUS) {
		nexus_private = (struct fast_dvma *)mp->dmai_nexus_private;
		nexus_funcptr = (struct dvma_ops *)nexus_private->ops;
		(void) (*nexus_funcptr->dvma_sync)(h, objindex, type);
	} else {
		ddi_dma_handle_t handle;

		handle = ((ddi_dma_handle_t *)mp->dmai_minfo)[objindex];
		(void) ddi_dma_sync(handle, 0, 0, type);
	}
}
コード例 #9
0
/*
 * e1000g_receive - main receive routine
 *
 * This routine will process packets received in an interrupt
 */
mblk_t *
e1000g_receive(e1000g_rx_ring_t *rx_ring, mblk_t **tail, uint_t sz)
{
	struct e1000_hw *hw;
	mblk_t *nmp;
	mblk_t *ret_mp;
	mblk_t *ret_nmp;
	struct e1000_rx_desc *current_desc;
	struct e1000_rx_desc *last_desc;
	p_rx_sw_packet_t packet;
	p_rx_sw_packet_t newpkt;
	uint16_t length;
	uint32_t pkt_count;
	uint32_t desc_count;
	boolean_t accept_frame;
	boolean_t end_of_packet;
	boolean_t need_copy;
	struct e1000g *Adapter;
	dma_buffer_t *rx_buf;
	uint16_t cksumflags;
	uint_t chain_sz = 0;
	e1000g_rx_data_t *rx_data;
	uint32_t max_size;
	uint32_t min_size;

	ret_mp = NULL;
	ret_nmp = NULL;
	pkt_count = 0;
	desc_count = 0;
	cksumflags = 0;

	Adapter = rx_ring->adapter;
	rx_data = rx_ring->rx_data;
	hw = &Adapter->shared;

	/* Sync the Rx descriptor DMA buffers */
	(void) ddi_dma_sync(rx_data->rbd_dma_handle,
	    0, 0, DDI_DMA_SYNC_FORKERNEL);

	if (e1000g_check_dma_handle(rx_data->rbd_dma_handle) != DDI_FM_OK) {
		ddi_fm_service_impact(Adapter->dip, DDI_SERVICE_DEGRADED);
		Adapter->e1000g_state |= E1000G_ERROR;
		return (NULL);
	}

	current_desc = rx_data->rbd_next;
	if (!(current_desc->status & E1000_RXD_STAT_DD)) {
		/*
		 * don't send anything up. just clear the RFD
		 */
		E1000G_DEBUG_STAT(rx_ring->stat_none);
		return (NULL);
	}

	max_size = Adapter->max_frame_size - ETHERFCSL - VLAN_TAGSZ;
	min_size = ETHERMIN;

	/*
	 * Loop through the receive descriptors starting at the last known
	 * descriptor owned by the hardware that begins a packet.
	 */
	while ((current_desc->status & E1000_RXD_STAT_DD) &&
	    (pkt_count < Adapter->rx_limit_onintr) &&
	    ((sz == E1000G_CHAIN_NO_LIMIT) || (chain_sz <= sz))) {

		desc_count++;
		/*
		 * Now this can happen in Jumbo frame situation.
		 */
		if (current_desc->status & E1000_RXD_STAT_EOP) {
			/* packet has EOP set */
			end_of_packet = B_TRUE;
		} else {
			/*
			 * If this received buffer does not have the
			 * End-Of-Packet bit set, the received packet
			 * will consume multiple buffers. We won't send this
			 * packet upstack till we get all the related buffers.
			 */
			end_of_packet = B_FALSE;
		}

		/*
		 * Get a pointer to the actual receive buffer
		 * The mp->b_rptr is mapped to The CurrentDescriptor
		 * Buffer Address.
		 */
		packet =
		    (p_rx_sw_packet_t)QUEUE_GET_HEAD(&rx_data->recv_list);
		ASSERT(packet != NULL);

		rx_buf = packet->rx_buf;

		length = current_desc->length;

#ifdef __sparc
		if (packet->dma_type == USE_DVMA)
			dvma_sync(rx_buf->dma_handle, 0,
			    DDI_DMA_SYNC_FORKERNEL);
		else
			(void) ddi_dma_sync(rx_buf->dma_handle,
			    E1000G_IPALIGNROOM, length,
			    DDI_DMA_SYNC_FORKERNEL);
#else
		(void) ddi_dma_sync(rx_buf->dma_handle,
		    E1000G_IPALIGNROOM, length,
		    DDI_DMA_SYNC_FORKERNEL);
#endif

		if (e1000g_check_dma_handle(
		    rx_buf->dma_handle) != DDI_FM_OK) {
			ddi_fm_service_impact(Adapter->dip,
			    DDI_SERVICE_DEGRADED);
			Adapter->e1000g_state |= E1000G_ERROR;

			goto rx_drop;
		}

		accept_frame = (current_desc->errors == 0) ||
		    ((current_desc->errors &
		    (E1000_RXD_ERR_TCPE | E1000_RXD_ERR_IPE)) != 0);

		if (hw->mac.type == e1000_82543) {
			unsigned char last_byte;

			last_byte =
			    *((unsigned char *)rx_buf->address + length - 1);

			if (TBI_ACCEPT(hw,
			    current_desc->status, current_desc->errors,
			    current_desc->length, last_byte,
			    Adapter->min_frame_size, Adapter->max_frame_size)) {

				e1000_tbi_adjust_stats(Adapter,
				    length, hw->mac.addr);

				length--;
				accept_frame = B_TRUE;
			} else if (e1000_tbi_sbp_enabled_82543(hw) &&
			    (current_desc->errors == E1000_RXD_ERR_CE)) {
				accept_frame = B_TRUE;
			}
		}

		/*
		 * Indicate the packet to the NOS if it was good.
		 * Normally, hardware will discard bad packets for us.
		 * Check for the packet to be a valid Ethernet packet
		 */
		if (!accept_frame) {
			/*
			 * error in incoming packet, either the packet is not a
			 * ethernet size packet, or the packet has an error. In
			 * either case, the packet will simply be discarded.
			 */
			E1000G_DEBUGLOG_0(Adapter, E1000G_INFO_LEVEL,
			    "Process Receive Interrupts: Error in Packet\n");

			E1000G_STAT(rx_ring->stat_error);
			/*
			 * Returning here as we are done here. There is
			 * no point in waiting for while loop to elapse
			 * and the things which were done. More efficient
			 * and less error prone...
			 */
			goto rx_drop;
		}

		/*
		 * If the Ethernet CRC is not stripped by the hardware,
		 * we need to strip it before sending it up to the stack.
		 */
		if (end_of_packet && !Adapter->strip_crc) {
			if (length > ETHERFCSL) {
				length -= ETHERFCSL;
			} else {
				/*
				 * If the fragment is smaller than the CRC,
				 * drop this fragment, do the processing of
				 * the end of the packet.
				 */
				ASSERT(rx_data->rx_mblk_tail != NULL);
				rx_data->rx_mblk_tail->b_wptr -=
				    ETHERFCSL - length;
				rx_data->rx_mblk_len -=
				    ETHERFCSL - length;

				QUEUE_POP_HEAD(&rx_data->recv_list);

				goto rx_end_of_packet;
			}
		}

		need_copy = B_TRUE;

		if (length <= Adapter->rx_bcopy_thresh)
			goto rx_copy;

		/*
		 * Get the pre-constructed mblk that was associated
		 * to the receive data buffer.
		 */
		if (packet->mp == NULL) {
			packet->mp = desballoc((unsigned char *)
			    rx_buf->address, length,
			    BPRI_MED, &packet->free_rtn);
		}

		if (packet->mp != NULL) {
			/*
			 * We have two sets of buffer pool. One associated with
			 * the Rxdescriptors and other a freelist buffer pool.
			 * Each time we get a good packet, Try to get a buffer
			 * from the freelist pool using e1000g_get_buf. If we
			 * get free buffer, then replace the descriptor buffer
			 * address with the free buffer we just got, and pass
			 * the pre-constructed mblk upstack. (note no copying)
			 *
			 * If we failed to get a free buffer, then try to
			 * allocate a new buffer(mp) and copy the recv buffer
			 * content to our newly allocated buffer(mp). Don't
			 * disturb the desriptor buffer address. (note copying)
			 */
			newpkt = e1000g_get_buf(rx_data);

			if (newpkt != NULL) {
				/*
				 * Get the mblk associated to the data,
				 * and strip it off the sw packet.
				 */
				nmp = packet->mp;
				packet->mp = NULL;
				atomic_inc_32(&packet->ref_cnt);

				/*
				 * Now replace old buffer with the new
				 * one we got from free list
				 * Both the RxSwPacket as well as the
				 * Receive Buffer Descriptor will now
				 * point to this new packet.
				 */
				packet = newpkt;

				current_desc->buffer_addr =
				    newpkt->rx_buf->dma_address;

				need_copy = B_FALSE;
			} else {
				/* EMPTY */
				E1000G_DEBUG_STAT(rx_ring->stat_no_freepkt);
			}
		}

rx_copy:
		if (need_copy) {
			/*
			 * No buffers available on free list,
			 * bcopy the data from the buffer and
			 * keep the original buffer. Dont want to
			 * do this.. Yack but no other way
			 */
			if ((nmp = allocb(length + E1000G_IPALIGNROOM,
			    BPRI_MED)) == NULL) {
				/*
				 * The system has no buffers available
				 * to send up the incoming packet, hence
				 * the packet will have to be processed
				 * when there're more buffers available.
				 */
				E1000G_STAT(rx_ring->stat_allocb_fail);
				goto rx_drop;
			}
			nmp->b_rptr += E1000G_IPALIGNROOM;
			nmp->b_wptr += E1000G_IPALIGNROOM;
			/*
			 * The free list did not have any buffers
			 * available, so, the received packet will
			 * have to be copied into a mp and the original
			 * buffer will have to be retained for future
			 * packet reception.
			 */
			bcopy(rx_buf->address, nmp->b_wptr, length);
		}

		/*
		 * The rx_sw_packet MUST be popped off the
		 * RxSwPacketList before either a putnext or freemsg
		 * is done on the mp that has now been created by the
		 * desballoc. If not, it is possible that the free
		 * routine will get called from the interrupt context
		 * and try to put this packet on the free list
		 */
		(p_rx_sw_packet_t)QUEUE_POP_HEAD(&rx_data->recv_list);

		ASSERT(nmp != NULL);
		nmp->b_wptr += length;

		if (rx_data->rx_mblk == NULL) {
			/*
			 *  TCP/UDP checksum offload and
			 *  IP checksum offload
			 */
			if (!(current_desc->status & E1000_RXD_STAT_IXSM)) {
				/*
				 * Check TCP/UDP checksum
				 */
				if ((current_desc->status &
				    E1000_RXD_STAT_TCPCS) &&
				    !(current_desc->errors &
				    E1000_RXD_ERR_TCPE))
					cksumflags |= HCK_FULLCKSUM |
					    HCK_FULLCKSUM_OK;
				/*
				 * Check IP Checksum
				 */
				if ((current_desc->status &
				    E1000_RXD_STAT_IPCS) &&
				    !(current_desc->errors &
				    E1000_RXD_ERR_IPE))
					cksumflags |= HCK_IPV4_HDRCKSUM;
			}
		}

		/*
		 * We need to maintain our packet chain in the global
		 * Adapter structure, for the Rx processing can end
		 * with a fragment that has no EOP set.
		 */
		if (rx_data->rx_mblk == NULL) {
			/* Get the head of the message chain */
			rx_data->rx_mblk = nmp;
			rx_data->rx_mblk_tail = nmp;
			rx_data->rx_mblk_len = length;
		} else {	/* Not the first packet */
			/* Continue adding buffers */
			rx_data->rx_mblk_tail->b_cont = nmp;
			rx_data->rx_mblk_tail = nmp;
			rx_data->rx_mblk_len += length;
		}
		ASSERT(rx_data->rx_mblk != NULL);
		ASSERT(rx_data->rx_mblk_tail != NULL);
		ASSERT(rx_data->rx_mblk_tail->b_cont == NULL);

		/*
		 * Now this MP is ready to travel upwards but some more
		 * fragments are coming.
		 * We will send packet upwards as soon as we get EOP
		 * set on the packet.
		 */
		if (!end_of_packet) {
			/*
			 * continue to get the next descriptor,
			 * Tail would be advanced at the end
			 */
			goto rx_next_desc;
		}

rx_end_of_packet:
		if (E1000G_IS_VLAN_PACKET(rx_data->rx_mblk->b_rptr))
			max_size = Adapter->max_frame_size - ETHERFCSL;

		if ((rx_data->rx_mblk_len > max_size) ||
		    (rx_data->rx_mblk_len < min_size)) {
			E1000G_STAT(rx_ring->stat_size_error);
			goto rx_drop;
		}

		/*
		 * Found packet with EOP
		 * Process the last fragment.
		 */
		if (cksumflags != 0) {
			(void) hcksum_assoc(rx_data->rx_mblk,
			    NULL, NULL, 0, 0, 0, 0, cksumflags, 0);
			cksumflags = 0;
		}

		/*
		 * Count packets that span multi-descriptors
		 */
		E1000G_DEBUG_STAT_COND(rx_ring->stat_multi_desc,
		    (rx_data->rx_mblk->b_cont != NULL));

		/*
		 * Append to list to send upstream
		 */
		if (ret_mp == NULL) {
			ret_mp = ret_nmp = rx_data->rx_mblk;
		} else {
			ret_nmp->b_next = rx_data->rx_mblk;
			ret_nmp = rx_data->rx_mblk;
		}
		ret_nmp->b_next = NULL;
		*tail = ret_nmp;
		chain_sz += length;

		rx_data->rx_mblk = NULL;
		rx_data->rx_mblk_tail = NULL;
		rx_data->rx_mblk_len = 0;

		pkt_count++;

rx_next_desc:
		/*
		 * Zero out the receive descriptors status
		 */
		current_desc->status = 0;

		if (current_desc == rx_data->rbd_last)
			rx_data->rbd_next = rx_data->rbd_first;
		else
			rx_data->rbd_next++;

		last_desc = current_desc;
		current_desc = rx_data->rbd_next;

		/*
		 * Put the buffer that we just indicated back
		 * at the end of our list
		 */
		QUEUE_PUSH_TAIL(&rx_data->recv_list,
		    &packet->Link);
	}	/* while loop */

	/* Sync the Rx descriptor DMA buffers */
	(void) ddi_dma_sync(rx_data->rbd_dma_handle,
	    0, 0, DDI_DMA_SYNC_FORDEV);

	/*
	 * Advance the E1000's Receive Queue #0 "Tail Pointer".
	 */
	E1000_WRITE_REG(hw, E1000_RDT(0),
	    (uint32_t)(last_desc - rx_data->rbd_first));

	if (e1000g_check_acc_handle(Adapter->osdep.reg_handle) != DDI_FM_OK) {
		ddi_fm_service_impact(Adapter->dip, DDI_SERVICE_DEGRADED);
		Adapter->e1000g_state |= E1000G_ERROR;
	}

	Adapter->rx_pkt_cnt = pkt_count;

	return (ret_mp);

rx_drop:
	/*
	 * Zero out the receive descriptors status
	 */
	current_desc->status = 0;

	/* Sync the Rx descriptor DMA buffers */
	(void) ddi_dma_sync(rx_data->rbd_dma_handle,
	    0, 0, DDI_DMA_SYNC_FORDEV);

	if (current_desc == rx_data->rbd_last)
		rx_data->rbd_next = rx_data->rbd_first;
	else
		rx_data->rbd_next++;

	last_desc = current_desc;

	(p_rx_sw_packet_t)QUEUE_POP_HEAD(&rx_data->recv_list);

	QUEUE_PUSH_TAIL(&rx_data->recv_list, &packet->Link);
	/*
	 * Reclaim all old buffers already allocated during
	 * Jumbo receives.....for incomplete reception
	 */
	if (rx_data->rx_mblk != NULL) {
		freemsg(rx_data->rx_mblk);
		rx_data->rx_mblk = NULL;
		rx_data->rx_mblk_tail = NULL;
		rx_data->rx_mblk_len = 0;
	}
	/*
	 * Advance the E1000's Receive Queue #0 "Tail Pointer".
	 */
	E1000_WRITE_REG(hw, E1000_RDT(0),
	    (uint32_t)(last_desc - rx_data->rbd_first));

	if (e1000g_check_acc_handle(Adapter->osdep.reg_handle) != DDI_FM_OK) {
		ddi_fm_service_impact(Adapter->dip, DDI_SERVICE_DEGRADED);
		Adapter->e1000g_state |= E1000G_ERROR;
	}

	return (ret_mp);
}
コード例 #10
0
int
ipw2200_load_fw(struct ipw2200_softc *sc, uint8_t *buf, size_t size)
{
	struct dma_region	dr[MAX_DR_NUM]; /* maximal, 64 * 4KB = 256KB */
	uint8_t			*p, *end, *v;
	uint32_t		mlen;
	uint32_t		src, dst, ctl, len, sum, off;
	uint32_t		sentinel;
	int			ntries, err, cnt, i;
	clock_t			clk = drv_usectohz(5000000);  /* 5 second */

	ipw2200_imem_put32(sc, 0x3000a0, 0x27000);

	p   = buf;
	end = p + size;

	cnt = 0;
	err = ipw2200_dma_region_alloc(sc, &dr[cnt], MAX_DR_SIZE, DDI_DMA_READ,
	    DDI_DMA_STREAMING);
	if (err != DDI_SUCCESS)
		goto fail0;
	off = 0;
	src = dr[cnt].dr_pbase;

	ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_ADDR, 0x27000);

	while (p < end) {
		dst = LE_32(*((uint32_t *)(uintptr_t)p)); p += 4;
		len = LE_32(*((uint32_t *)(uintptr_t)p)); p += 4;
		v = p;
		p += len;
		IPW2200_DBG(IPW2200_DBG_FW, (sc->sc_dip, CE_CONT,
		    "ipw2200_load_fw(): dst=0x%x,len=%u\n", dst, len));

		while (len > 0) {
			/*
			 * if no DMA region is available, allocate a new one
			 */
			if (off == dr[cnt].dr_size) {
				cnt++;
				if (cnt >= MAX_DR_NUM) {
					IPW2200_WARN((sc->sc_dip, CE_WARN,
					    "ipw2200_load_fw(): "
					    "maximum %d DRs is reached\n",
					    cnt));
					cnt--; /* only free alloced DMA */
					goto fail1;
				}
				err = ipw2200_dma_region_alloc(sc, &dr[cnt],
				    MAX_DR_SIZE, DDI_DMA_WRITE,
				    DDI_DMA_STREAMING);
				if (err != DDI_SUCCESS) {
					cnt--; /* only free alloced DMA */
					goto fail1;
				}
				off = 0;
				src = dr[cnt].dr_pbase;
			}
			mlen = min(IPW2200_CB_MAXDATALEN, len);
			mlen = min(mlen, dr[cnt].dr_size - off);

			(void) memcpy(dr[cnt].dr_base + off, v, mlen);
			(void) ddi_dma_sync(dr[cnt].dr_hnd, off, mlen,
			    DDI_DMA_SYNC_FORDEV);

			ctl = IPW2200_CB_DEFAULT_CTL | mlen;
			sum = ctl ^ src ^ dst;
			/*
			 * write a command
			 */
			ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, ctl);
			ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, src);
			ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, dst);
			ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, sum);

			off += mlen;
			src += mlen;
			dst += mlen;
			v   += mlen;
			len -= mlen;
		}
	}

	sentinel = ipw2200_csr_get32(sc, IPW2200_CSR_AUTOINC_ADDR);
	ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, 0);

	IPW2200_DBG(IPW2200_DBG_FW, (sc->sc_dip, CE_CONT,
	    "ipw2200_load_fw(): sentinel=%x\n", sentinel));

	ipw2200_csr_put32(sc, IPW2200_CSR_RST,
	    ~(IPW2200_RST_MASTER_DISABLED | IPW2200_RST_STOP_MASTER)
	    & ipw2200_csr_get32(sc, IPW2200_CSR_RST));

	ipw2200_imem_put32(sc, 0x3000a4, 0x540100);
	for (ntries = 0; ntries < 400; ntries++) {
		uint32_t val;
		val = ipw2200_imem_get32(sc, 0x3000d0);
		if (val >= sentinel)
			break;
		drv_usecwait(100);
	}
	if (ntries == 400) {
		IPW2200_WARN((sc->sc_dip, CE_WARN,
		    "ipw2200_load_fw(): timeout processing command blocks\n"));
		goto fail1;
	}

	mutex_enter(&sc->sc_ilock);

	ipw2200_imem_put32(sc, 0x3000a4, 0x540c00);

	/*
	 * enable all interrupts
	 */
	ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, IPW2200_INTR_MASK_ALL);

	/*
	 * tell the adapter to initialize the firmware,
	 * just simply set it to 0
	 */
	ipw2200_csr_put32(sc, IPW2200_CSR_RST, 0);
	ipw2200_csr_put32(sc, IPW2200_CSR_CTL,
	    ipw2200_csr_get32(sc, IPW2200_CSR_CTL) |
	    IPW2200_CTL_ALLOW_STANDBY);

	/*
	 * wait for interrupt to notify fw initialization is done
	 */
	sc->sc_fw_ok = 0;
	while (!sc->sc_fw_ok) {
		/*
		 * There is an enhancement! we just change from 1s to 5s
		 */
		if (cv_reltimedwait(&sc->sc_fw_cond, &sc->sc_ilock, clk,
		    TR_CLOCK_TICK) < 0)
			break;
	}
	mutex_exit(&sc->sc_ilock);

	if (!sc->sc_fw_ok) {
		IPW2200_WARN((sc->sc_dip, CE_WARN,
		    "ipw2200_load_fw(): firmware(%u) load failed!", size));
		goto fail1;
	}

	for (i = 0; i <= cnt; i++)
		ipw2200_dma_region_free(&dr[i]);

	return (DDI_SUCCESS);

fail1:
	IPW2200_WARN((sc->sc_dip, CE_WARN,
	    "ipw2200_load_fw(): DMA allocation failed, cnt=%d\n", cnt));
	for (i = 0; i <= cnt; i++)
		ipw2200_dma_region_free(&dr[i]);
fail0:
	return (DDI_FAILURE);
}
コード例 #11
0
/*
 * tavor_srq_modify()
 *    Context: Can be called only from user or kernel context.
 */
int
tavor_srq_modify(tavor_state_t *state, tavor_srqhdl_t srq, uint_t size,
    uint_t *real_size, uint_t sleepflag)
{
	tavor_qalloc_info_t	new_srqinfo, old_srqinfo;
	tavor_rsrc_t		*mtt, *mpt, *old_mtt;
	tavor_bind_info_t	bind;
	tavor_bind_info_t	old_bind;
	tavor_rsrc_pool_info_t	*rsrc_pool;
	tavor_mrhdl_t		mr;
	tavor_hw_mpt_t		mpt_entry;
	tavor_wrid_entry_t	*wre_new, *wre_old;
	uint64_t		mtt_ddrbaseaddr, mtt_addr;
	uint64_t		srq_desc_off;
	uint32_t		*buf, srq_old_bufsz;
	uint32_t		wqesz;
	uint_t			max_srq_size;
	uint_t			dma_xfer_mode, mtt_pgsize_bits;
	uint_t			srq_sync, log_srq_size, maxprot;
	uint_t			wq_location;
	int			status;
	char			*errormsg;

	TAVOR_TNF_ENTER(tavor_srq_modify);

	/*
	 * Check the "inddr" flag.  This flag tells the driver whether or not
	 * the SRQ's work queues should be come from normal system memory or
	 * whether they should be allocated from DDR memory.
	 */
	wq_location = state->ts_cfg_profile->cp_srq_wq_inddr;

	/*
	 * If size requested is larger than device capability, return
	 * Insufficient Resources
	 */
	max_srq_size = (1 << state->ts_cfg_profile->cp_log_max_srq_sz);
	if (size > max_srq_size) {
		TNF_PROBE_0(tavor_srq_modify_size_larger_than_maxsize,
		    TAVOR_TNF_ERROR, "");
		TAVOR_TNF_EXIT(tavor_srq_modify);
		return (IBT_HCA_WR_EXCEEDED);
	}

	/*
	 * Calculate the appropriate size for the SRQ.
	 * Note:  All Tavor SRQs must be a power-of-2 in size.  Also
	 * they may not be any smaller than TAVOR_SRQ_MIN_SIZE.  This step
	 * is to round the requested size up to the next highest power-of-2
	 */
	size = max(size, TAVOR_SRQ_MIN_SIZE);
	log_srq_size = highbit(size);
	if ((size & (size - 1)) == 0) {
		log_srq_size = log_srq_size - 1;
	}

	/*
	 * Next we verify that the rounded-up size is valid (i.e. consistent
	 * with the device limits and/or software-configured limits).
	 */
	if (log_srq_size > state->ts_cfg_profile->cp_log_max_srq_sz) {
		/* Set "status" and "errormsg" and goto failure */
		TAVOR_TNF_FAIL(IBT_HCA_WR_EXCEEDED, "max SRQ size");
		goto srqmodify_fail;
	}

	/*
	 * Allocate the memory for newly resized Shared Receive Queue.
	 *
	 * Note: If SRQ is not user-mappable, then it may come from either
	 * kernel system memory or from HCA-attached local DDR memory.
	 *
	 * Note2: We align this queue on a pagesize boundary.  This is required
	 * to make sure that all the resulting IB addresses will start at 0,
	 * for a zero-based queue.  By making sure we are aligned on at least a
	 * page, any offset we use into our queue will be the same as it was
	 * when we allocated it at tavor_srq_alloc() time.
	 */
	wqesz = (1 << srq->srq_wq_log_wqesz);
	new_srqinfo.qa_size = (1 << log_srq_size) * wqesz;
	new_srqinfo.qa_alloc_align = PAGESIZE;
	new_srqinfo.qa_bind_align  = PAGESIZE;
	if (srq->srq_is_umap) {
		new_srqinfo.qa_location = TAVOR_QUEUE_LOCATION_USERLAND;
	} else {
		new_srqinfo.qa_location = wq_location;
	}
	status = tavor_queue_alloc(state, &new_srqinfo, sleepflag);
	if (status != DDI_SUCCESS) {
		/* Set "status" and "errormsg" and goto failure */
		TAVOR_TNF_FAIL(IBT_INSUFF_RESOURCE, "failed srq");
		goto srqmodify_fail;
	}
	buf = (uint32_t *)new_srqinfo.qa_buf_aligned;
	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*buf))

	/*
	 * Allocate the memory for the new WRE list.  This will be used later
	 * when we resize the wridlist based on the new SRQ size.
	 */
	wre_new = (tavor_wrid_entry_t *)kmem_zalloc((1 << log_srq_size) *
	    sizeof (tavor_wrid_entry_t), sleepflag);
	if (wre_new == NULL) {
		/* Set "status" and "errormsg" and goto failure */
		TAVOR_TNF_FAIL(IBT_INSUFF_RESOURCE,
		    "failed wre_new alloc");
		goto srqmodify_fail;
	}

	/*
	 * Fill in the "bind" struct.  This struct provides the majority
	 * of the information that will be used to distinguish between an
	 * "addr" binding (as is the case here) and a "buf" binding (see
	 * below).  The "bind" struct is later passed to tavor_mr_mem_bind()
	 * which does most of the "heavy lifting" for the Tavor memory
	 * registration routines.
	 */
	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(bind))
	bzero(&bind, sizeof (tavor_bind_info_t));
	bind.bi_type  = TAVOR_BINDHDL_VADDR;
	bind.bi_addr  = (uint64_t)(uintptr_t)buf;
	bind.bi_len   = new_srqinfo.qa_size;
	bind.bi_as    = NULL;
	bind.bi_flags = sleepflag == TAVOR_SLEEP ? IBT_MR_SLEEP :
	    IBT_MR_NOSLEEP | IBT_MR_ENABLE_LOCAL_WRITE;
	if (srq->srq_is_umap) {
		bind.bi_bypass = state->ts_cfg_profile->cp_iommu_bypass;
	} else {
		if (wq_location == TAVOR_QUEUE_LOCATION_NORMAL) {
			bind.bi_bypass =
			    state->ts_cfg_profile->cp_iommu_bypass;
			dma_xfer_mode =
			    state->ts_cfg_profile->cp_streaming_consistent;
			if (dma_xfer_mode == DDI_DMA_STREAMING) {
				bind.bi_flags |= IBT_MR_NONCOHERENT;
			}
		} else {
			bind.bi_bypass = TAVOR_BINDMEM_BYPASS;
		}
	}
	status = tavor_mr_mtt_bind(state, &bind, new_srqinfo.qa_dmahdl, &mtt,
	    &mtt_pgsize_bits);
	if (status != DDI_SUCCESS) {
		/* Set "status" and "errormsg" and goto failure */
		TAVOR_TNF_FAIL(status, "failed mtt bind");
		kmem_free(wre_new, srq->srq_wq_bufsz *
		    sizeof (tavor_wrid_entry_t));
		tavor_queue_free(state, &new_srqinfo);
		goto srqmodify_fail;
	}

	/*
	 * Calculate the offset between the kernel virtual address space
	 * and the IB virtual address space.  This will be used when
	 * posting work requests to properly initialize each WQE.
	 *
	 * Note: bind addr is zero-based (from alloc) so we calculate the
	 * correct new offset here.
	 */
	bind.bi_addr = bind.bi_addr & ((1 << mtt_pgsize_bits) - 1);
	srq_desc_off = (uint64_t)(uintptr_t)new_srqinfo.qa_buf_aligned -
	    (uint64_t)bind.bi_addr;

	/*
	 * Get the base address for the MTT table.  This will be necessary
	 * below when we are modifying the MPT entry.
	 */
	rsrc_pool = &state->ts_rsrc_hdl[TAVOR_MTT];
	mtt_ddrbaseaddr = (uint64_t)(uintptr_t)rsrc_pool->rsrc_ddr_offset;

	/*
	 * Fill in the MPT entry.  This is the final step before passing
	 * ownership of the MPT entry to the Tavor hardware.  We use all of
	 * the information collected/calculated above to fill in the
	 * requisite portions of the MPT.
	 */
	bzero(&mpt_entry, sizeof (tavor_hw_mpt_t));
	mpt_entry.reg_win_len	= bind.bi_len;
	mtt_addr = mtt_ddrbaseaddr + (mtt->tr_indx << TAVOR_MTT_SIZE_SHIFT);
	mpt_entry.mttseg_addr_h = mtt_addr >> 32;
	mpt_entry.mttseg_addr_l = mtt_addr >> 6;

	/*
	 * Now we grab the SRQ lock.  Since we will be updating the actual
	 * SRQ location and the producer/consumer indexes, we should hold
	 * the lock.
	 *
	 * We do a TAVOR_NOSLEEP here (and below), though, because we are
	 * holding the "srq_lock" and if we got raised to interrupt level
	 * by priority inversion, we would not want to block in this routine
	 * waiting for success.
	 */
	mutex_enter(&srq->srq_lock);

	/*
	 * Copy old entries to new buffer
	 */
	srq_old_bufsz = srq->srq_wq_bufsz;
	bcopy(srq->srq_wq_buf, buf, srq_old_bufsz * wqesz);

	/* Determine if later ddi_dma_sync will be necessary */
	srq_sync = TAVOR_SRQ_IS_SYNC_REQ(state, srq->srq_wqinfo);

	/* Sync entire "new" SRQ for use by hardware (if necessary) */
	if (srq_sync) {
		(void) ddi_dma_sync(bind.bi_dmahdl, 0,
		    new_srqinfo.qa_size, DDI_DMA_SYNC_FORDEV);
	}

	/*
	 * Setup MPT information for use in the MODIFY_MPT command
	 */
	mr = srq->srq_mrhdl;
	mutex_enter(&mr->mr_lock);
	mpt = srq->srq_mrhdl->mr_mptrsrcp;

	/*
	 * MODIFY_MPT
	 *
	 * If this fails for any reason, then it is an indication that
	 * something (either in HW or SW) has gone seriously wrong.  So we
	 * print a warning message and return.
	 */
	status = tavor_modify_mpt_cmd_post(state, &mpt_entry, mpt->tr_indx,
	    TAVOR_CMD_MODIFY_MPT_RESIZESRQ, sleepflag);
	if (status != TAVOR_CMD_SUCCESS) {
		cmn_err(CE_CONT, "Tavor: MODIFY_MPT command failed: %08x\n",
		    status);
		TNF_PROBE_1(tavor_mr_common_reg_sw2hw_mpt_cmd_fail,
		    TAVOR_TNF_ERROR, "", tnf_uint, status, status);
		TAVOR_TNF_FAIL(status, "MODIFY_MPT command failed");
		(void) tavor_mr_mtt_unbind(state, &srq->srq_mrhdl->mr_bindinfo,
		    srq->srq_mrhdl->mr_mttrsrcp);
		kmem_free(wre_new, srq->srq_wq_bufsz *
		    sizeof (tavor_wrid_entry_t));
		tavor_queue_free(state, &new_srqinfo);
		mutex_exit(&mr->mr_lock);
		mutex_exit(&srq->srq_lock);
		return (ibc_get_ci_failure(0));
	}

	/*
	 * Update the Tavor Shared Receive Queue handle with all the new
	 * information.  At the same time, save away all the necessary
	 * information for freeing up the old resources
	 */
	old_srqinfo	   = srq->srq_wqinfo;
	old_mtt		   = srq->srq_mrhdl->mr_mttrsrcp;
	bcopy(&srq->srq_mrhdl->mr_bindinfo, &old_bind,
	    sizeof (tavor_bind_info_t));

	/* Now set the new info */
	srq->srq_wqinfo	   = new_srqinfo;
	srq->srq_wq_buf	   = buf;
	srq->srq_wq_bufsz  = (1 << log_srq_size);
	bcopy(&bind, &srq->srq_mrhdl->mr_bindinfo, sizeof (tavor_bind_info_t));
	srq->srq_mrhdl->mr_mttrsrcp = mtt;
	srq->srq_desc_off  = srq_desc_off;
	srq->srq_real_sizes.srq_wr_sz = (1 << log_srq_size);

	/* Update MR mtt pagesize */
	mr->mr_logmttpgsz = mtt_pgsize_bits;
	mutex_exit(&mr->mr_lock);

#ifdef __lock_lint
	mutex_enter(&srq->srq_wrid_wql->wql_lock);
#else
	if (srq->srq_wrid_wql != NULL) {
		mutex_enter(&srq->srq_wrid_wql->wql_lock);
	}
#endif

	/*
	 * Initialize new wridlist, if needed.
	 *
	 * If a wridlist already is setup on an SRQ (the QP associated with an
	 * SRQ has moved "from_reset") then we must update this wridlist based
	 * on the new SRQ size.  We allocate the new size of Work Request ID
	 * Entries, copy over the old entries to the new list, and
	 * re-initialize the srq wridlist in non-umap case
	 */
	wre_old = NULL;
	if (srq->srq_wridlist != NULL) {
		wre_old = srq->srq_wridlist->wl_wre;

		bcopy(wre_old, wre_new, srq_old_bufsz *
		    sizeof (tavor_wrid_entry_t));

		/* Setup new sizes in wre */
		srq->srq_wridlist->wl_wre = wre_new;
		srq->srq_wridlist->wl_size = srq->srq_wq_bufsz;

		if (!srq->srq_is_umap) {
			tavor_wrid_list_srq_init(srq->srq_wridlist, srq,
			    srq_old_bufsz);
		}
	}

#ifdef __lock_lint
	mutex_exit(&srq->srq_wrid_wql->wql_lock);
#else
	if (srq->srq_wrid_wql != NULL) {
		mutex_exit(&srq->srq_wrid_wql->wql_lock);
	}
#endif

	/*
	 * If "old" SRQ was a user-mappable SRQ that is currently mmap()'d out
	 * to a user process, then we need to call devmap_devmem_remap() to
	 * invalidate the mapping to the SRQ memory.  We also need to
	 * invalidate the SRQ tracking information for the user mapping.
	 *
	 * Note: On failure, the remap really shouldn't ever happen.  So, if it
	 * does, it is an indication that something has gone seriously wrong.
	 * So we print a warning message and return error (knowing, of course,
	 * that the "old" SRQ memory will be leaked)
	 */
	if ((srq->srq_is_umap) && (srq->srq_umap_dhp != NULL)) {
		maxprot = (PROT_READ | PROT_WRITE | PROT_USER);
		status = devmap_devmem_remap(srq->srq_umap_dhp,
		    state->ts_dip, 0, 0, srq->srq_wqinfo.qa_size, maxprot,
		    DEVMAP_MAPPING_INVALID, NULL);
		if (status != DDI_SUCCESS) {
			mutex_exit(&srq->srq_lock);
			TAVOR_WARNING(state, "failed in SRQ memory "
			    "devmap_devmem_remap()");
			/* We can, however, free the memory for old wre */
			if (wre_old != NULL) {
				kmem_free(wre_old, srq_old_bufsz *
				    sizeof (tavor_wrid_entry_t));
			}
			TAVOR_TNF_EXIT(tavor_srq_modify);
			return (ibc_get_ci_failure(0));
		}
		srq->srq_umap_dhp = (devmap_cookie_t)NULL;
	}

	/*
	 * Drop the SRQ lock now.  The only thing left to do is to free up
	 * the old resources.
	 */
	mutex_exit(&srq->srq_lock);

	/*
	 * Unbind the MTT entries.
	 */
	status = tavor_mr_mtt_unbind(state, &old_bind, old_mtt);
	if (status != DDI_SUCCESS) {
		TAVOR_WARNING(state, "failed to unbind old SRQ memory");
		/* Set "status" and "errormsg" and goto failure */
		TAVOR_TNF_FAIL(ibc_get_ci_failure(0),
		    "failed to unbind (old)");
		goto srqmodify_fail;
	}

	/* Free the memory for old wre */
	if (wre_old != NULL) {
		kmem_free(wre_old, srq_old_bufsz *
		    sizeof (tavor_wrid_entry_t));
	}

	/* Free the memory for the old SRQ */
	tavor_queue_free(state, &old_srqinfo);

	/*
	 * Fill in the return arguments (if necessary).  This includes the
	 * real new completion queue size.
	 */
	if (real_size != NULL) {
		*real_size = (1 << log_srq_size);
	}

	TAVOR_TNF_EXIT(tavor_srq_modify);
	return (DDI_SUCCESS);

srqmodify_fail:
	TNF_PROBE_1(tavor_srq_modify_fail, TAVOR_TNF_ERROR, "",
	    tnf_string, msg, errormsg);
	TAVOR_TNF_EXIT(tavor_srq_modify);
	return (status);
}
コード例 #12
0
ファイル: hxge_send.c プロジェクト: bahamas10/openzfs
static int
hxge_start(p_hxge_t hxgep, p_tx_ring_t tx_ring_p, p_mblk_t mp)
{
	int 			dma_status, status = 0;
	p_tx_desc_t 		tx_desc_ring_vp;
	hpi_handle_t		hpi_desc_handle;
	hxge_os_dma_handle_t 	tx_desc_dma_handle;
	p_tx_desc_t 		tx_desc_p;
	p_tx_msg_t 		tx_msg_ring;
	p_tx_msg_t 		tx_msg_p;
	tx_desc_t		tx_desc, *tmp_desc_p;
	tx_desc_t		sop_tx_desc, *sop_tx_desc_p;
	p_tx_pkt_header_t	hdrp;
	p_tx_pkt_hdr_all_t	pkthdrp;
	uint8_t			npads = 0;
	uint64_t 		dma_ioaddr;
	uint32_t		dma_flags;
	int			last_bidx;
	uint8_t 		*b_rptr;
	caddr_t 		kaddr;
	uint32_t		nmblks;
	uint32_t		ngathers;
	uint32_t		clen;
	int 			len;
	uint32_t		pkt_len, pack_len, min_len;
	uint32_t		bcopy_thresh;
	int 			i, cur_index, sop_index;
	uint16_t		tail_index;
	boolean_t		tail_wrap = B_FALSE;
	hxge_dma_common_t	desc_area;
	hxge_os_dma_handle_t 	dma_handle;
	ddi_dma_cookie_t 	dma_cookie;
	hpi_handle_t		hpi_handle;
	p_mblk_t 		nmp;
	p_mblk_t		t_mp;
	uint32_t 		ncookies;
	boolean_t 		good_packet;
	boolean_t 		mark_mode = B_FALSE;
	p_hxge_stats_t 		statsp;
	p_hxge_tx_ring_stats_t	tdc_stats;
	t_uscalar_t 		start_offset = 0;
	t_uscalar_t 		stuff_offset = 0;
	t_uscalar_t 		end_offset = 0;
	t_uscalar_t 		value = 0;
	t_uscalar_t 		cksum_flags = 0;
	boolean_t		cksum_on = B_FALSE;
	uint32_t		boff = 0;
	uint64_t		tot_xfer_len = 0, tmp_len = 0;
	boolean_t		header_set = B_FALSE;
	tdc_tdr_kick_t		kick;
	uint32_t		offset;
#ifdef HXGE_DEBUG
	p_tx_desc_t 		tx_desc_ring_pp;
	p_tx_desc_t 		tx_desc_pp;
	tx_desc_t		*save_desc_p;
	int			dump_len;
	int			sad_len;
	uint64_t		sad;
	int			xfer_len;
	uint32_t		msgsize;
#endif

	HXGE_DEBUG_MSG((hxgep, TX_CTL,
	    "==> hxge_start: tx dma channel %d", tx_ring_p->tdc));
	HXGE_DEBUG_MSG((hxgep, TX_CTL,
	    "==> hxge_start: Starting tdc %d desc pending %d",
	    tx_ring_p->tdc, tx_ring_p->descs_pending));

	statsp = hxgep->statsp;

	if (hxgep->statsp->port_stats.lb_mode == hxge_lb_normal) {
		if (!statsp->mac_stats.link_up) {
			freemsg(mp);
			HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_start: "
			    "link not up or LB mode"));
			goto hxge_start_fail1;
		}
	}

	mac_hcksum_get(mp, &start_offset, &stuff_offset, &end_offset, &value,
	    &cksum_flags);
	if (!HXGE_IS_VLAN_PACKET(mp->b_rptr)) {
		start_offset += sizeof (ether_header_t);
		stuff_offset += sizeof (ether_header_t);
	} else {
		start_offset += sizeof (struct ether_vlan_header);
		stuff_offset += sizeof (struct ether_vlan_header);
	}

	if (cksum_flags & HCK_PARTIALCKSUM) {
		HXGE_DEBUG_MSG((hxgep, TX_CTL,
		    "==> hxge_start: mp $%p len %d "
		    "cksum_flags 0x%x (partial checksum) ",
		    mp, MBLKL(mp), cksum_flags));
		cksum_on = B_TRUE;
	}

	MUTEX_ENTER(&tx_ring_p->lock);
start_again:
	ngathers = 0;
	sop_index = tx_ring_p->wr_index;
#ifdef	HXGE_DEBUG
	if (tx_ring_p->descs_pending) {
		HXGE_DEBUG_MSG((hxgep, TX_CTL,
		    "==> hxge_start: desc pending %d ",
		    tx_ring_p->descs_pending));
	}

	dump_len = (int)(MBLKL(mp));
	dump_len = (dump_len > 128) ? 128: dump_len;

	HXGE_DEBUG_MSG((hxgep, TX_CTL,
	    "==> hxge_start: tdc %d: dumping ...: b_rptr $%p "
	    "(Before header reserve: ORIGINAL LEN %d)",
	    tx_ring_p->tdc, mp->b_rptr, dump_len));

	HXGE_DEBUG_MSG((hxgep, TX_CTL,
	    "==> hxge_start: dump packets (IP ORIGINAL b_rptr $%p): %s",
	    mp->b_rptr, hxge_dump_packet((char *)mp->b_rptr, dump_len)));
#endif

	tdc_stats = tx_ring_p->tdc_stats;
	mark_mode = (tx_ring_p->descs_pending &&
	    ((tx_ring_p->tx_ring_size - tx_ring_p->descs_pending) <
	    hxge_tx_minfree));

	HXGE_DEBUG_MSG((hxgep, TX_CTL,
	    "TX Descriptor ring is channel %d mark mode %d",
	    tx_ring_p->tdc, mark_mode));

	if (!hxge_txdma_reclaim(hxgep, tx_ring_p, hxge_tx_minfree)) {
		HXGE_DEBUG_MSG((hxgep, TX_CTL,
		    "TX Descriptor ring is full: channel %d", tx_ring_p->tdc));
		HXGE_DEBUG_MSG((hxgep, TX_CTL,
		    "TX Descriptor ring is full: channel %d", tx_ring_p->tdc));
		(void) atomic_cas_32((uint32_t *)&tx_ring_p->queueing, 0, 1);
		tdc_stats->tx_no_desc++;
		MUTEX_EXIT(&tx_ring_p->lock);
		status = 1;
		goto hxge_start_fail1;
	}

	nmp = mp;
	i = sop_index = tx_ring_p->wr_index;
	nmblks = 0;
	ngathers = 0;
	pkt_len = 0;
	pack_len = 0;
	clen = 0;
	last_bidx = -1;
	good_packet = B_TRUE;

	desc_area = tx_ring_p->tdc_desc;
	hpi_handle = desc_area.hpi_handle;
	hpi_desc_handle.regh = (hxge_os_acc_handle_t)
	    DMA_COMMON_ACC_HANDLE(desc_area);
	hpi_desc_handle.hxgep = hxgep;
	tx_desc_ring_vp = (p_tx_desc_t)DMA_COMMON_VPTR(desc_area);
#ifdef	HXGE_DEBUG
#if defined(__i386)
	tx_desc_ring_pp = (p_tx_desc_t)(uint32_t)DMA_COMMON_IOADDR(desc_area);
#else
	tx_desc_ring_pp = (p_tx_desc_t)DMA_COMMON_IOADDR(desc_area);
#endif
#endif
	tx_desc_dma_handle = (hxge_os_dma_handle_t)DMA_COMMON_HANDLE(desc_area);
	tx_msg_ring = tx_ring_p->tx_msg_ring;

	HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_start: wr_index %d i %d",
	    sop_index, i));

#ifdef	HXGE_DEBUG
	msgsize = msgdsize(nmp);
	HXGE_DEBUG_MSG((hxgep, TX_CTL,
	    "==> hxge_start(1): wr_index %d i %d msgdsize %d",
	    sop_index, i, msgsize));
#endif
	/*
	 * The first 16 bytes of the premapped buffer are reserved
	 * for header. No padding will be used.
	 */
	pkt_len = pack_len = boff = TX_PKT_HEADER_SIZE;
	if (hxge_tx_use_bcopy) {
		bcopy_thresh = (hxge_bcopy_thresh - TX_PKT_HEADER_SIZE);
	} else {
		bcopy_thresh = (TX_BCOPY_SIZE - TX_PKT_HEADER_SIZE);
	}
	while (nmp) {
		good_packet = B_TRUE;
		b_rptr = nmp->b_rptr;
		len = MBLKL(nmp);
		if (len <= 0) {
			nmp = nmp->b_cont;
			continue;
		}
		nmblks++;

		HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_start(1): nmblks %d "
		    "len %d pkt_len %d pack_len %d",
		    nmblks, len, pkt_len, pack_len));
		/*
		 * Hardware limits the transfer length to 4K.
		 * If len is more than 4K, we need to break
		 * nmp into two chunks: Make first chunk smaller
		 * than 4K. The second chunk will be broken into
		 * less than 4K (if needed) during the next pass.
		 */
		if (len > (TX_MAX_TRANSFER_LENGTH - TX_PKT_HEADER_SIZE)) {
			if ((t_mp = dupb(nmp)) != NULL) {
				nmp->b_wptr = nmp->b_rptr +
				    (TX_MAX_TRANSFER_LENGTH -
				    TX_PKT_HEADER_SIZE);
				t_mp->b_rptr = nmp->b_wptr;
				t_mp->b_cont = nmp->b_cont;
				nmp->b_cont = t_mp;
				len = MBLKL(nmp);
			} else {
				good_packet = B_FALSE;
				goto hxge_start_fail2;
			}
		}
		tx_desc.value = 0;
		tx_desc_p = &tx_desc_ring_vp[i];
#ifdef	HXGE_DEBUG
		tx_desc_pp = &tx_desc_ring_pp[i];
#endif
		tx_msg_p = &tx_msg_ring[i];
#if defined(__i386)
		hpi_desc_handle.regp = (uint32_t)tx_desc_p;
#else
		hpi_desc_handle.regp = (uint64_t)tx_desc_p;
#endif
		if (!header_set &&
		    ((!hxge_tx_use_bcopy && (len > TX_BCOPY_SIZE)) ||
		    (len >= bcopy_thresh))) {
			header_set = B_TRUE;
			bcopy_thresh += TX_PKT_HEADER_SIZE;
			boff = 0;
			pack_len = 0;
			kaddr = (caddr_t)DMA_COMMON_VPTR(tx_msg_p->buf_dma);
			hdrp = (p_tx_pkt_header_t)kaddr;
			clen = pkt_len;
			dma_handle = tx_msg_p->buf_dma_handle;
			dma_ioaddr = DMA_COMMON_IOADDR(tx_msg_p->buf_dma);
			offset = tx_msg_p->offset_index * hxge_bcopy_thresh;
			(void) ddi_dma_sync(dma_handle,
			    offset, hxge_bcopy_thresh, DDI_DMA_SYNC_FORDEV);

			tx_msg_p->flags.dma_type = USE_BCOPY;
			goto hxge_start_control_header_only;
		}

		pkt_len += len;
		pack_len += len;

		HXGE_DEBUG_MSG((hxgep, TX_CTL,
		    "==> hxge_start(3): desc entry %d DESC IOADDR $%p "
		    "desc_vp $%p tx_desc_p $%p desc_pp $%p tx_desc_pp $%p "
		    "len %d pkt_len %d pack_len %d",
		    i,
		    DMA_COMMON_IOADDR(desc_area),
		    tx_desc_ring_vp, tx_desc_p,
		    tx_desc_ring_pp, tx_desc_pp,
		    len, pkt_len, pack_len));

		if (len < bcopy_thresh) {
			HXGE_DEBUG_MSG((hxgep, TX_CTL,
			    "==> hxge_start(4): USE BCOPY: "));
			if (hxge_tx_tiny_pack) {
				uint32_t blst = TXDMA_DESC_NEXT_INDEX(i, -1,
				    tx_ring_p->tx_wrap_mask);
				HXGE_DEBUG_MSG((hxgep, TX_CTL,
				    "==> hxge_start(5): pack"));
				if ((pack_len <= bcopy_thresh) &&
				    (last_bidx == blst)) {
					HXGE_DEBUG_MSG((hxgep, TX_CTL,
					    "==> hxge_start: pack(6) "
					    "(pkt_len %d pack_len %d)",
					    pkt_len, pack_len));
					i = blst;
					tx_desc_p = &tx_desc_ring_vp[i];
#ifdef	HXGE_DEBUG
					tx_desc_pp = &tx_desc_ring_pp[i];
#endif
					tx_msg_p = &tx_msg_ring[i];
					boff = pack_len - len;
					ngathers--;
				} else if (pack_len > bcopy_thresh &&
				    header_set) {
					pack_len = len;
					boff = 0;
					bcopy_thresh = hxge_bcopy_thresh;
					HXGE_DEBUG_MSG((hxgep, TX_CTL,
					    "==> hxge_start(7): > max NEW "
					    "bcopy thresh %d "
					    "pkt_len %d pack_len %d(next)",
					    bcopy_thresh, pkt_len, pack_len));
				}
				last_bidx = i;
			}
			kaddr = (caddr_t)DMA_COMMON_VPTR(tx_msg_p->buf_dma);
			if ((boff == TX_PKT_HEADER_SIZE) && (nmblks == 1)) {
				hdrp = (p_tx_pkt_header_t)kaddr;
				header_set = B_TRUE;
				HXGE_DEBUG_MSG((hxgep, TX_CTL,
				    "==> hxge_start(7_x2): "
				    "pkt_len %d pack_len %d (new hdrp $%p)",
				    pkt_len, pack_len, hdrp));
			}
			tx_msg_p->flags.dma_type = USE_BCOPY;
			kaddr += boff;
			HXGE_DEBUG_MSG((hxgep, TX_CTL,
			    "==> hxge_start(8): USE BCOPY: before bcopy "
			    "DESC IOADDR $%p entry %d bcopy packets %d "
			    "bcopy kaddr $%p bcopy ioaddr (SAD) $%p "
			    "bcopy clen %d bcopy boff %d",
			    DMA_COMMON_IOADDR(desc_area), i,
			    tdc_stats->tx_hdr_pkts, kaddr, dma_ioaddr,
			    clen, boff));
			HXGE_DEBUG_MSG((hxgep, TX_CTL,
			    "==> hxge_start: 1USE BCOPY: "));
			HXGE_DEBUG_MSG((hxgep, TX_CTL,
			    "==> hxge_start: 2USE BCOPY: "));
			HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_start: "
			    "last USE BCOPY: copy from b_rptr $%p "
			    "to KADDR $%p (len %d offset %d",
			    b_rptr, kaddr, len, boff));
			bcopy(b_rptr, kaddr, len);
#ifdef	HXGE_DEBUG
			dump_len = (len > 128) ? 128: len;
			HXGE_DEBUG_MSG((hxgep, TX_CTL,
			    "==> hxge_start: dump packets "
			    "(After BCOPY len %d)"
			    "(b_rptr $%p): %s", len, nmp->b_rptr,
			    hxge_dump_packet((char *)nmp->b_rptr,
			    dump_len)));
#endif
			dma_handle = tx_msg_p->buf_dma_handle;
			dma_ioaddr = DMA_COMMON_IOADDR(tx_msg_p->buf_dma);
			offset = tx_msg_p->offset_index * hxge_bcopy_thresh;
			(void) ddi_dma_sync(dma_handle,
			    offset, hxge_bcopy_thresh, DDI_DMA_SYNC_FORDEV);
			clen = len + boff;
			tdc_stats->tx_hdr_pkts++;
			HXGE_DEBUG_MSG((hxgep, TX_CTL, "==> hxge_start(9): "
			    "USE BCOPY: DESC IOADDR $%p entry %d "
			    "bcopy packets %d bcopy kaddr $%p "
			    "bcopy ioaddr (SAD) $%p bcopy clen %d "
			    "bcopy boff %d",
			    DMA_COMMON_IOADDR(desc_area), i,
			    tdc_stats->tx_hdr_pkts, kaddr, dma_ioaddr,
			    clen, boff));
		} else {
			HXGE_DEBUG_MSG((hxgep, TX_CTL,
			    "==> hxge_start(12): USE DVMA: len %d", len));
			tx_msg_p->flags.dma_type = USE_DMA;
			dma_flags = DDI_DMA_WRITE;
			if (len < hxge_dma_stream_thresh) {
				dma_flags |= DDI_DMA_CONSISTENT;
			} else {
				dma_flags |= DDI_DMA_STREAMING;
			}

			dma_handle = tx_msg_p->dma_handle;
			dma_status = ddi_dma_addr_bind_handle(dma_handle, NULL,
			    (caddr_t)b_rptr, len, dma_flags,
			    DDI_DMA_DONTWAIT, NULL,
			    &dma_cookie, &ncookies);
			if (dma_status == DDI_DMA_MAPPED) {
				dma_ioaddr = dma_cookie.dmac_laddress;
				len = (int)dma_cookie.dmac_size;
				clen = (uint32_t)dma_cookie.dmac_size;
				HXGE_DEBUG_MSG((hxgep, TX_CTL,
				    "==> hxge_start(12_1): "
				    "USE DVMA: len %d clen %d ngathers %d",
				    len, clen, ngathers));
#if defined(__i386)
				hpi_desc_handle.regp = (uint32_t)tx_desc_p;
#else
				hpi_desc_handle.regp = (uint64_t)tx_desc_p;
#endif
				while (ncookies > 1) {
					ngathers++;
					/*
					 * this is the fix for multiple
					 * cookies, which are basically
					 * a descriptor entry, we don't set
					 * SOP bit as well as related fields
					 */

					(void) hpi_txdma_desc_gather_set(
					    hpi_desc_handle, &tx_desc,
					    (ngathers -1), mark_mode,
					    ngathers, dma_ioaddr, clen);
					tx_msg_p->tx_msg_size = clen;
					HXGE_DEBUG_MSG((hxgep, TX_CTL,
					    "==> hxge_start:  DMA "
					    "ncookie %d ngathers %d "
					    "dma_ioaddr $%p len %d"
					    "desc $%p descp $%p (%d)",
					    ncookies, ngathers,
					    dma_ioaddr, clen,
					    *tx_desc_p, tx_desc_p, i));

					ddi_dma_nextcookie(dma_handle,
					    &dma_cookie);
					dma_ioaddr = dma_cookie.dmac_laddress;

					len = (int)dma_cookie.dmac_size;
					clen = (uint32_t)dma_cookie.dmac_size;
					HXGE_DEBUG_MSG((hxgep, TX_CTL,
					    "==> hxge_start(12_2): "
					    "USE DVMA: len %d clen %d ",
					    len, clen));

					i = TXDMA_DESC_NEXT_INDEX(i, 1,
					    tx_ring_p->tx_wrap_mask);
					tx_desc_p = &tx_desc_ring_vp[i];

					hpi_desc_handle.regp =
#if defined(__i386)
					    (uint32_t)tx_desc_p;
#else
						(uint64_t)tx_desc_p;
#endif
					tx_msg_p = &tx_msg_ring[i];
					tx_msg_p->flags.dma_type = USE_NONE;
					tx_desc.value = 0;
					ncookies--;
				}
				tdc_stats->tx_ddi_pkts++;
				HXGE_DEBUG_MSG((hxgep, TX_CTL,
				    "==> hxge_start: DMA: ddi packets %d",
				    tdc_stats->tx_ddi_pkts));
			} else {
				HXGE_ERROR_MSG((hxgep, HXGE_ERR_CTL,
				    "dma mapping failed for %d "
				    "bytes addr $%p flags %x (%d)",
				    len, b_rptr, status, status));
				good_packet = B_FALSE;
				tdc_stats->tx_dma_bind_fail++;
				tx_msg_p->flags.dma_type = USE_NONE;
				status = 1;
				goto hxge_start_fail2;
			}
		} /* ddi dvma */

		nmp = nmp->b_cont;
hxge_start_control_header_only:
#if defined(__i386)
		hpi_desc_handle.regp = (uint32_t)tx_desc_p;
#else
		hpi_desc_handle.regp = (uint64_t)tx_desc_p;
#endif
		ngathers++;

		if (ngathers == 1) {
#ifdef	HXGE_DEBUG
			save_desc_p = &sop_tx_desc;
#endif
			sop_tx_desc_p = &sop_tx_desc;
			sop_tx_desc_p->value = 0;
			sop_tx_desc_p->bits.tr_len = clen;
			sop_tx_desc_p->bits.sad = dma_ioaddr >> 32;
			sop_tx_desc_p->bits.sad_l = dma_ioaddr & 0xffffffff;
		} else {
コード例 #13
0
/*
 * audioixp_alloc_port()
 *
 * Description:
 *	This routine allocates the DMA handles and the memory for the
 *	DMA engines to use.  It also configures the BDL lists properly
 *	for use.
 *
 * Arguments:
 *	dev_info_t	*dip	Pointer to the device's devinfo
 *
 * Returns:
 *	DDI_SUCCESS		Registers successfully mapped
 *	DDI_FAILURE		Registers not successfully mapped
 */
static int
audioixp_alloc_port(audioixp_state_t *statep, int num)
{
	ddi_dma_cookie_t	cookie;
	uint_t			count;
	int			dir;
	unsigned		caps;
	char			*prop;
	audio_dev_t		*adev;
	audioixp_port_t		*port;
	uint32_t		paddr;
	int			rc;
	dev_info_t		*dip;
	audioixp_bd_entry_t	*bdentry;

	adev = statep->adev;
	dip = statep->dip;

	port = kmem_zalloc(sizeof (*port), KM_SLEEP);
	port->statep = statep;
	port->started = B_FALSE;
	port->num = num;

	switch (num) {
	case IXP_REC:
		statep->rec_port = port;
		prop = "record-interrupts";
		dir = DDI_DMA_READ;
		caps = ENGINE_INPUT_CAP;
		port->sync_dir = DDI_DMA_SYNC_FORKERNEL;
		port->nchan = 2;
		break;
	case IXP_PLAY:
		statep->play_port = port;
		prop = "play-interrupts";
		dir = DDI_DMA_WRITE;
		caps = ENGINE_OUTPUT_CAP;
		port->sync_dir = DDI_DMA_SYNC_FORDEV;
		/* This could possibly be conditionalized */
		port->nchan = 6;
		break;
	default:
		audio_dev_warn(adev, "bad port number (%d)!", num);
		return (DDI_FAILURE);
	}

	port->intrs = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
	    DDI_PROP_DONTPASS, prop, IXP_INTS);

	/* make sure the values are good */
	if (port->intrs < IXP_MIN_INTS) {
		audio_dev_warn(adev, "%s too low, %d, resetting to %d",
		    prop, port->intrs, IXP_INTS);
		port->intrs = IXP_INTS;
	} else if (port->intrs > IXP_MAX_INTS) {
		audio_dev_warn(adev, "%s too high, %d, resetting to %d",
		    prop, port->intrs, IXP_INTS);
		port->intrs = IXP_INTS;
	}

	/*
	 * Figure out how much space we need.  Sample rate is 48kHz, and
	 * we need to store 8 chunks.  (Note that this means that low
	 * interrupt frequencies will require more RAM.)
	 */
	port->fragfr = 48000 / port->intrs;
	port->fragfr = IXP_ROUNDUP(port->fragfr, IXP_MOD_SIZE);
	port->fragsz = port->fragfr * port->nchan * 2;
	port->samp_size = port->fragsz * IXP_BD_NUMS;

	/* allocate dma handle */
	rc = ddi_dma_alloc_handle(dip, &sample_buf_dma_attr, DDI_DMA_SLEEP,
	    NULL, &port->samp_dmah);
	if (rc != DDI_SUCCESS) {
		audio_dev_warn(adev, "ddi_dma_alloc_handle failed: %d", rc);
		return (DDI_FAILURE);
	}
	/* allocate DMA buffer */
	rc = ddi_dma_mem_alloc(port->samp_dmah, port->samp_size, &buf_attr,
	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->samp_kaddr,
	    &port->samp_size, &port->samp_acch);
	if (rc == DDI_FAILURE) {
		audio_dev_warn(adev, "dma_mem_alloc failed");
		return (DDI_FAILURE);
	}

	/* bind DMA buffer */
	rc = ddi_dma_addr_bind_handle(port->samp_dmah, NULL,
	    port->samp_kaddr, port->samp_size, dir|DDI_DMA_CONSISTENT,
	    DDI_DMA_SLEEP, NULL, &cookie, &count);
	if ((rc != DDI_DMA_MAPPED) || (count != 1)) {
		audio_dev_warn(adev,
		    "ddi_dma_addr_bind_handle failed: %d", rc);
		return (DDI_FAILURE);
	}
	port->samp_paddr = cookie.dmac_address;

	/*
	 * now, from here we allocate DMA memory for buffer descriptor list.
	 * we allocate adjacent DMA memory for all DMA engines.
	 */
	rc = ddi_dma_alloc_handle(dip, &bdlist_dma_attr, DDI_DMA_SLEEP,
	    NULL, &port->bdl_dmah);
	if (rc != DDI_SUCCESS) {
		audio_dev_warn(adev, "ddi_dma_alloc_handle(bdlist) failed");
		return (DDI_FAILURE);
	}

	/*
	 * we allocate all buffer descriptors lists in continuous dma memory.
	 */
	port->bdl_size = sizeof (audioixp_bd_entry_t) * IXP_BD_NUMS;
	rc = ddi_dma_mem_alloc(port->bdl_dmah, port->bdl_size,
	    &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
	    &port->bdl_kaddr, &port->bdl_size, &port->bdl_acch);
	if (rc != DDI_SUCCESS) {
		audio_dev_warn(adev, "ddi_dma_mem_alloc(bdlist) failed");
		return (DDI_FAILURE);
	}

	rc = ddi_dma_addr_bind_handle(port->bdl_dmah, NULL, port->bdl_kaddr,
	    port->bdl_size, DDI_DMA_WRITE|DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
	    NULL, &cookie, &count);
	if ((rc != DDI_DMA_MAPPED) || (count != 1)) {
		audio_dev_warn(adev, "addr_bind_handle failed");
		return (DDI_FAILURE);
	}
	port->bdl_paddr = cookie.dmac_address;

	/*
	 * Wire up the BD list.
	 */
	paddr = port->samp_paddr;
	bdentry = (void *)port->bdl_kaddr;

	for (int i = 0; i < IXP_BD_NUMS; i++) {

		/* set base address of buffer */
		ddi_put32(port->bdl_acch, &bdentry->buf_base, paddr);
		ddi_put16(port->bdl_acch, &bdentry->status, 0);
		ddi_put16(port->bdl_acch, &bdentry->buf_len, port->fragsz / 4);
		ddi_put32(port->bdl_acch, &bdentry->next, port->bdl_paddr +
		    (((i + 1) % IXP_BD_NUMS) * sizeof (audioixp_bd_entry_t)));
		paddr += port->fragsz;
		bdentry++;
	}
	(void) ddi_dma_sync(port->bdl_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);

	port->engine = audio_engine_alloc(&audioixp_engine_ops, caps);
	if (port->engine == NULL) {
		audio_dev_warn(adev, "audio_engine_alloc failed");
		return (DDI_FAILURE);
	}

	audio_engine_set_private(port->engine, port);
	audio_dev_add_engine(adev, port->engine);

	return (DDI_SUCCESS);
}
コード例 #14
0
ファイル: oce_tx.c プロジェクト: apprisi/illumos-gate
/*
 * function to xmit  Single packet over the wire
 *
 * wq - pointer to WQ
 * mp - Pointer to packet chain
 *
 * return pointer to the packet
 */
mblk_t *
oce_send_packet(struct oce_wq *wq, mblk_t *mp)
{
	struct oce_nic_hdr_wqe *wqeh;
	struct oce_dev *dev;
	struct ether_header *eh;
	struct ether_vlan_header *evh;
	int32_t num_wqes;
	uint16_t etype;
	uint32_t ip_offset;
	uint32_t csum_flags = 0;
	boolean_t use_copy = B_FALSE;
	boolean_t tagged   = B_FALSE;
	uint16_t  vlan_tag;
	uint32_t  reg_value = 0;
	oce_wqe_desc_t *wqed = NULL;
	mblk_t *nmp = NULL;
	mblk_t *tmp = NULL;
	uint32_t pkt_len = 0;
	int num_mblks = 0;
	int ret = 0;
	uint32_t mss = 0;
	uint32_t flags = 0;
	int len = 0;

	/* retrieve the adap priv struct ptr */
	dev = wq->parent;

	/* check if we have enough free slots */
	if (wq->wq_free < dev->tx_reclaim_threshold) {
		(void) oce_process_tx_compl(wq, B_FALSE);
	}
	if (wq->wq_free < OCE_MAX_TX_HDL) {
		return (mp);
	}

	/* check if we should copy */
	for (tmp = mp; tmp != NULL; tmp = tmp->b_cont) {
		pkt_len += MBLKL(tmp);
		num_mblks++;
	}

	if (pkt_len == 0 || num_mblks == 0) {
		freemsg(mp);
		return (NULL);
	}

	/* retrieve LSO information */
	mac_lso_get(mp, &mss, &flags);

	/* get the offload flags */
	mac_hcksum_get(mp, NULL, NULL, NULL, NULL, &csum_flags);

	/* restrict the mapped segment to wat we support */
	if (num_mblks  > OCE_MAX_TX_HDL) {
		nmp = msgpullup(mp, -1);
		if (nmp == NULL) {
			atomic_inc_32(&wq->pkt_drops);
			freemsg(mp);
			return (NULL);
		}
		/* Reset it to new collapsed mp */
		freemsg(mp);
		mp = nmp;
	}

	/* Get the packet descriptor for Tx */
	wqed = kmem_cache_alloc(wq->wqed_cache, KM_NOSLEEP);
	if (wqed == NULL) {
		atomic_inc_32(&wq->pkt_drops);
		freemsg(mp);
		return (NULL);
	}
	eh = (struct ether_header *)(void *)mp->b_rptr;
	if (ntohs(eh->ether_type) == VLAN_TPID) {
		evh = (struct ether_vlan_header *)(void *)mp->b_rptr;
		tagged = B_TRUE;
		etype = ntohs(evh->ether_type);
		ip_offset = sizeof (struct ether_vlan_header);
		pkt_len -= VTAG_SIZE;
		vlan_tag = ntohs(evh->ether_tci);
		oce_remove_vtag(mp);
	} else {
		etype = ntohs(eh->ether_type);
		ip_offset = sizeof (struct ether_header);
	}

	/* Save the WQ pointer */
	wqed->wq = wq;
	wqed->frag_idx = 1; /* index zero is always header */
	wqed->frag_cnt = 0;
	wqed->nhdl = 0;
	wqed->mp = NULL;
	OCE_LIST_LINK_INIT(&wqed->link);

	/* If entire packet is less than the copy limit  just do copy */
	if (pkt_len < dev->tx_bcopy_limit) {
		use_copy = B_TRUE;
		ret = oce_bcopy_wqe(wq, wqed, mp, pkt_len);
	} else {
		/* copy or dma map the individual fragments */
		for (nmp = mp; nmp != NULL; nmp = nmp->b_cont) {
			len = MBLKL(nmp);
			if (len == 0) {
				continue;
			}
			if (len < dev->tx_bcopy_limit) {
				ret = oce_bcopy_wqe(wq, wqed, nmp, len);
			} else {
				ret = oce_map_wqe(wq, wqed, nmp, len);
			}
			if (ret != 0)
				break;
		}
	}

	/*
	 * Any failure other than insufficient Q entries
	 * drop the packet
	 */
	if (ret != 0) {
		oce_free_wqed(wq, wqed);
		atomic_inc_32(&wq->pkt_drops);
		freemsg(mp);
		return (NULL);
	}

	wqeh = (struct oce_nic_hdr_wqe *)&wqed->frag[0];
	bzero(wqeh, sizeof (struct oce_nic_hdr_wqe));

	/* fill rest of wqe header fields based on packet */
	if (flags & HW_LSO) {
		wqeh->u0.s.lso = B_TRUE;
		wqeh->u0.s.lso_mss = mss;
	}
	if (csum_flags & HCK_FULLCKSUM) {
		uint8_t *proto;
		if (etype == ETHERTYPE_IP) {
			proto = (uint8_t *)(void *)
			    (mp->b_rptr + ip_offset);
			if (proto[9] == 6)
				/* IPPROTO_TCP */
				wqeh->u0.s.tcpcs = B_TRUE;
			else if (proto[9] == 17)
				/* IPPROTO_UDP */
				wqeh->u0.s.udpcs = B_TRUE;
		}
	}

	if (csum_flags & HCK_IPV4_HDRCKSUM)
		wqeh->u0.s.ipcs = B_TRUE;
	if (tagged) {
		wqeh->u0.s.vlan = B_TRUE;
		wqeh->u0.s.vlan_tag = vlan_tag;
	}

	wqeh->u0.s.complete = B_TRUE;
	wqeh->u0.s.event = B_TRUE;
	wqeh->u0.s.crc = B_TRUE;
	wqeh->u0.s.total_length = pkt_len;

	num_wqes = wqed->frag_cnt + 1;

	/* h/w expects even no. of WQEs */
	if (num_wqes & 0x1) {
		bzero(&wqed->frag[num_wqes], sizeof (struct oce_nic_frag_wqe));
		num_wqes++;
	}
	wqed->wqe_cnt = (uint16_t)num_wqes;
	wqeh->u0.s.num_wqe = num_wqes;
	DW_SWAP(u32ptr(&wqed->frag[0]), (wqed->wqe_cnt * NIC_WQE_SIZE));

	mutex_enter(&wq->tx_lock);
	if (num_wqes > wq->wq_free) {
		atomic_inc_32(&wq->tx_deferd);
		mutex_exit(&wq->tx_lock);
		goto wqe_fail;
	}
	atomic_add_32(&wq->wq_free, -num_wqes);

	/* fill the wq for adapter */
	oce_fill_ring_descs(wq, wqed);

	/* Set the mp pointer in the wqe descriptor */
	if (use_copy == B_FALSE) {
		wqed->mp = mp;
	}
	/* Add the packet desc to list to be retrieved during cmpl */
	OCE_LIST_INSERT_TAIL(&wq->wqe_desc_list,  wqed);
	(void) ddi_dma_sync(wq->ring->dbuf->dma_handle, 0, 0,
	    DDI_DMA_SYNC_FORDEV);

	/* ring tx doorbell */
	reg_value = (num_wqes << 16) | wq->wq_id;
	/* Ring the door bell  */
	OCE_DB_WRITE32(dev, PD_TXULP_DB, reg_value);
	mutex_exit(&wq->tx_lock);
	if (oce_fm_check_acc_handle(dev, dev->db_handle) != DDI_FM_OK) {
		ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED);
	}

	/* free mp if copied or packet chain collapsed */
	if (use_copy == B_TRUE) {
		freemsg(mp);
	}
	return (NULL);

wqe_fail:

	if (tagged) {
		oce_insert_vtag(mp, vlan_tag);
	}
	oce_free_wqed(wq, wqed);
	return (mp);
} /* oce_send_packet */
コード例 #15
0
/*
 * RAID Action for System Shutdown. This request uses the dedicated TM slot to
 * avoid a call to mptsas_save_cmd.  Since Solaris requires that the mutex is
 * not held during the mptsas_quiesce function, this RAID action must not use
 * the normal code path of requests and replies.
 */
void
mptsas_raid_action_system_shutdown(mptsas_t *mpt)
{
	pMpi2RaidActionRequest_t	action;
	uint8_t				ir_active = FALSE, reply_type;
	uint8_t				function, found_reply = FALSE;
	uint16_t			SMID, action_type;
	mptsas_slots_t			*slots = mpt->m_active;
	int				config, vol;
	mptsas_cmd_t			*cmd;
	uint32_t			request_desc_low, reply_addr;
	int				cnt;
	pMpi2ReplyDescriptorsUnion_t	reply_desc_union;
	pMPI2DefaultReply_t		reply;
	pMpi2AddressReplyDescriptor_t	address_reply;

	/*
	 * Before doing the system shutdown RAID Action, make sure that the IOC
	 * supports IR and make sure there is a valid volume for the request.
	 */
	if (mpt->m_ir_capable) {
		for (config = 0; (config < slots->m_num_raid_configs) &&
		    (!ir_active); config++) {
			for (vol = 0; vol < MPTSAS_MAX_RAIDVOLS; vol++) {
				if (slots->m_raidconfig[config].m_raidvol[vol].
				    m_israid) {
					ir_active = TRUE;
					break;
				}
			}
		}
	}
	if (!ir_active) {
		return;
	}

	/*
	 * If TM slot is already being used (highly unlikely), show message and
	 * don't issue the RAID action.
	 */
	if (slots->m_slot[MPTSAS_TM_SLOT(mpt)] != NULL) {
		mptsas_log(mpt, CE_WARN, "RAID Action slot in use.  Cancelling"
		    " System Shutdown RAID Action.\n");
		return;
	}

	/*
	 * Create the cmd and put it in the dedicated TM slot.
	 */
	cmd = &(mpt->m_event_task_mgmt.m_event_cmd);
	bzero((caddr_t)cmd, sizeof (*cmd));
	cmd->cmd_pkt = NULL;
	cmd->cmd_slot = MPTSAS_TM_SLOT(mpt);
	slots->m_slot[MPTSAS_TM_SLOT(mpt)] = cmd;

	/*
	 * Form message for raid action.
	 */
	action = (pMpi2RaidActionRequest_t)(mpt->m_req_frame +
	    (mpt->m_req_frame_size * cmd->cmd_slot));
	bzero(action, mpt->m_req_frame_size);
	action->Function = MPI2_FUNCTION_RAID_ACTION;
	action->Action = MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED;

	/*
	 * Send RAID Action.
	 */
	(void) ddi_dma_sync(mpt->m_dma_req_frame_hdl, 0, 0,
	    DDI_DMA_SYNC_FORDEV);
	request_desc_low = (cmd->cmd_slot << 16) +
	    MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
	MPTSAS_START_CMD(mpt, request_desc_low, 0);

	/*
	 * Even though reply does not matter because the system is shutting
	 * down, wait no more than 5 seconds here to get the reply just because
	 * we don't want to leave it hanging if it's coming.  Poll because
	 * interrupts are disabled when this function is called.
	 */
	for (cnt = 0; cnt < 5000; cnt++) {
		/*
		 * Check for a reply.
		 */
		(void) ddi_dma_sync(mpt->m_dma_post_queue_hdl, 0, 0,
		    DDI_DMA_SYNC_FORCPU);

		reply_desc_union = (pMpi2ReplyDescriptorsUnion_t)
		    MPTSAS_GET_NEXT_REPLY(mpt, mpt->m_post_index);

		if (ddi_get32(mpt->m_acc_post_queue_hdl,
		    &reply_desc_union->Words.Low) == 0xFFFFFFFF ||
		    ddi_get32(mpt->m_acc_post_queue_hdl,
		    &reply_desc_union->Words.High) == 0xFFFFFFFF) {
			drv_usecwait(1000);
			continue;
		}

		/*
		 * There is a reply.  If it's not an address reply, ignore it.
		 */
		reply_type = ddi_get8(mpt->m_acc_post_queue_hdl,
		    &reply_desc_union->Default.ReplyFlags);
		reply_type &= MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
		if (reply_type != MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY) {
			goto clear_and_continue;
		}

		/*
		 * SMID must be the TM slot since that's what we're using for
		 * this RAID action.  If not, ignore this reply.
		 */
		address_reply =
		    (pMpi2AddressReplyDescriptor_t)reply_desc_union;
		SMID = ddi_get16(mpt->m_acc_post_queue_hdl,
		    &address_reply->SMID);
		if (SMID != MPTSAS_TM_SLOT(mpt)) {
			goto clear_and_continue;
		}

		/*
		 * If reply frame is not in the proper range ignore it.
		 */
		reply_addr = ddi_get32(mpt->m_acc_post_queue_hdl,
		    &address_reply->ReplyFrameAddress);
		if ((reply_addr < mpt->m_reply_frame_dma_addr) ||
		    (reply_addr >= (mpt->m_reply_frame_dma_addr +
		    (mpt->m_reply_frame_size * mpt->m_free_queue_depth))) ||
		    ((reply_addr - mpt->m_reply_frame_dma_addr) %
		    mpt->m_reply_frame_size != 0)) {
			goto clear_and_continue;
		}

		/*
		 * If not a RAID action reply ignore it.
		 */
		(void) ddi_dma_sync(mpt->m_dma_reply_frame_hdl, 0, 0,
		    DDI_DMA_SYNC_FORCPU);
		reply = (pMPI2DefaultReply_t)(mpt->m_reply_frame +
		    (reply_addr - mpt->m_reply_frame_dma_addr));
		function = ddi_get8(mpt->m_acc_reply_frame_hdl,
		    &reply->Function);
		if (function != MPI2_FUNCTION_RAID_ACTION) {
			goto clear_and_continue;
		}

		/*
		 * Finally, make sure this is the System Shutdown RAID action.
		 * If not, ignore reply.
		 */
		action_type = ddi_get16(mpt->m_acc_reply_frame_hdl,
		    &reply->FunctionDependent1);
		if (action_type !=
		    MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED) {
			goto clear_and_continue;
		}
		found_reply = TRUE;

clear_and_continue:
		/*
		 * Clear the reply descriptor for re-use and increment index.
		 */
		ddi_put64(mpt->m_acc_post_queue_hdl,
		    &((uint64_t *)(void *)mpt->m_post_queue)[mpt->m_post_index],
		    0xFFFFFFFFFFFFFFFF);
		(void) ddi_dma_sync(mpt->m_dma_post_queue_hdl, 0, 0,
		    DDI_DMA_SYNC_FORDEV);

		/*
		 * Update the global reply index and keep looking for the
		 * reply if not found yet.
		 */
		if (++mpt->m_post_index == mpt->m_post_queue_depth) {
			mpt->m_post_index = 0;
		}
		ddi_put32(mpt->m_datap, &mpt->m_reg->ReplyPostHostIndex,
		    mpt->m_post_index);
		if (!found_reply) {
			continue;
		}

		break;
	}

	/*
	 * clear the used slot as the last step.
	 */
	slots->m_slot[MPTSAS_TM_SLOT(mpt)] = NULL;
}
コード例 #16
0
/*
 * hci1394_ixl_intr_check_done()
 *    checks if context has stopped, or if able to match hardware location
 *    with an expected IXL program location.
 */
static int
hci1394_ixl_intr_check_done(hci1394_state_t *soft_statep,
    hci1394_iso_ctxt_t *ctxtp)
{
	ixl1394_command_t   *ixlp;
	hci1394_xfer_ctl_t  *xferctlp;
	uint_t		    ixldepth;
	hci1394_xfer_ctl_dma_t *dma;
	ddi_acc_handle_t    acc_hdl;
	ddi_dma_handle_t    dma_hdl;
	uint32_t	    desc_status;
	hci1394_desc_t	    *hcidescp;
	off_t		    hcidesc_off;
	int		    err;
	uint32_t	    dma_cmd_cur_loc;
	uint32_t	    dma_cmd_last_loc;
	uint32_t	    dma_loc_check_enabled;
	uint32_t	    dmastartp;
	uint32_t	    dmaendp;

	uint_t		    rem_dma_skips;
	uint16_t	    skipmode;
	uint16_t	    skipdepth;
	ixl1394_command_t   *skipdestp;
	ixl1394_command_t   *skipxferp;

	TNF_PROBE_0_DEBUG(hci1394_ixl_intr_check_done_enter,
	    HCI1394_TNF_HAL_STACK_ISOCH, "");

	/*
	 * start looking through the IXL list from the xfer start command where
	 * we last left off (for composite opcodes, need to start from the
	 * appropriate depth).
	 */

	ixlp = ctxtp->ixl_execp;
	ixldepth = ctxtp->ixl_exec_depth;

	/* control struct for xfer start IXL command */
	xferctlp = (hci1394_xfer_ctl_t *)ixlp->compiler_privatep;
	dma = &xferctlp->dma[ixldepth];

	/* determine if dma location checking is enabled */
	if ((dma_loc_check_enabled =
	    (ctxtp->ctxt_flags & HCI1394_ISO_CTXT_CMDREG)) != 0) {

		/* if so, get current dma command location */
		dma_cmd_last_loc = 0xFFFFFFFF;

		while ((dma_cmd_cur_loc = HCI1394_ISOCH_CTXT_CMD_PTR(
		    soft_statep, ctxtp)) != dma_cmd_last_loc) {

			/* retry get until location register stabilizes */
			dma_cmd_last_loc = dma_cmd_cur_loc;
		}
	}

	/*
	 * compare the (bound) address of the DMA descriptor corresponding to
	 * the current xfer IXL command against the current value in the
	 * DMA location register.  If exists and if matches, then
	 *    if context stopped, return stopped, else return done.
	 *
	 * The dma start address is the first address of the descriptor block.
	 * Since "Z" is a count of 16-byte descriptors in the block, calculate
	 * the end address by adding Z*16 to the start addr.
	 */
	dmastartp = dma->dma_bound & ~DESC_Z_MASK;
	dmaendp = dmastartp + ((dma->dma_bound & DESC_Z_MASK) << 4);

	if (dma_loc_check_enabled &&
	    ((dma_cmd_cur_loc >= dmastartp) && (dma_cmd_cur_loc < dmaendp))) {

		if (HCI1394_ISOCH_CTXT_ACTIVE(soft_statep, ctxtp) == 0) {
			TNF_PROBE_1_DEBUG(hci1394_ixl_intr_check_done_exit,
			    HCI1394_TNF_HAL_STACK_ISOCH, "", tnf_string, msg,
			    "CHECK_STOP");
			return (IXL_CHECK_STOP);
		}

		TNF_PROBE_1_DEBUG(hci1394_ixl_intr_check_done_exit,
		    HCI1394_TNF_HAL_STACK_ISOCH, "", tnf_string, msg,
		    "CHECK_DONE");
		return (IXL_CHECK_DONE);
	}

	/*
	 * if receive mode:
	 */
	if ((ixlp->ixl_opcode & IXL1394_OPF_ONXMIT) == 0)  {
		/*
		 * if context stopped, return stopped, else,
		 * if there is no current dma location reg, return done
		 * else return location indeterminate
		 */
		if (HCI1394_ISOCH_CTXT_ACTIVE(soft_statep, ctxtp) == 0) {
			TNF_PROBE_1_DEBUG(hci1394_ixl_intr_check_done_exit,
			    HCI1394_TNF_HAL_STACK_ISOCH, "", tnf_string, msg,
			    "CHECK_STOP");
			return (IXL_CHECK_STOP);
		}
		if (!dma_loc_check_enabled) {
			TNF_PROBE_1_DEBUG(hci1394_ixl_intr_check_done_exit,
			    HCI1394_TNF_HAL_STACK_ISOCH, "", tnf_string, msg,
			    "CHECK_DONE");
			return (IXL_CHECK_DONE);
		}

		TNF_PROBE_1_DEBUG(hci1394_ixl_intr_check_done_exit,
		    HCI1394_TNF_HAL_STACK_ISOCH, "", tnf_string, msg,
		    "CHECK_LOST");
		return (IXL_CHECK_LOST);
	}

	/*
	 * else is xmit mode:
	 * check status of current xfer IXL command's dma descriptor
	 */
	acc_hdl  = dma->dma_buf->bi_handle;
	dma_hdl  = dma->dma_buf->bi_dma_handle;
	hcidescp = (hci1394_desc_t *)dma->dma_descp;
	hcidesc_off = (off_t)hcidescp - (off_t)dma->dma_buf->bi_kaddr;

	/* Sync the descriptor before we get the status */
	err = ddi_dma_sync(dma_hdl, hcidesc_off, sizeof (hci1394_desc_t),
	    DDI_DMA_SYNC_FORCPU);
	if (err != DDI_SUCCESS) {
		TNF_PROBE_1(hci1394_ixl_intr_check_done_error,
		    HCI1394_TNF_HAL_ERROR_ISOCH, "", tnf_string, errmsg,
		    "dma_sync() failed");
	}
	desc_status = ddi_get32(acc_hdl, &hcidescp->status);

	if ((desc_status & DESC_XFER_ACTIVE_MASK) != 0) {

		/*
		 * if status is now set here, return skipped, to cause calling
		 * function to continue, even though location hasn't changed
		 */
		TNF_PROBE_1_DEBUG(hci1394_ixl_intr_check_done_exit,
		    HCI1394_TNF_HAL_STACK_ISOCH, "", tnf_string, msg,
		    "CHECK_SKIP");
		return (IXL_CHECK_SKIP);
	}

	/*
	 * At this point, we have gotten to a DMA descriptor with an empty
	 * status.  This is not enough information however to determine that
	 * we've found all processed DMA descriptors because during cycle-lost
	 * conditions, the HW will skip over some descriptors without writing
	 * status.  So we have to look ahead until we're convinced that the HW
	 * hasn't jumped ahead.
	 *
	 * Follow the IXL skip-to links until find one whose status is set
	 * or until dma location register (if any) matches an xfer IXL
	 * command's dma location or until have examined max_dma_skips
	 * IXL commands.
	 */
	rem_dma_skips = ctxtp->max_dma_skips;

	while (rem_dma_skips-- > 0) {

		/*
		 * get either IXL command specific or
		 * system default skipmode info
		 */
		skipdepth = 0;
		if (xferctlp->skipmodep != NULL) {
			skipmode  = xferctlp->skipmodep->skipmode;
			skipdestp = xferctlp->skipmodep->label;
			skipxferp = (ixl1394_command_t *)
			    xferctlp->skipmodep->compiler_privatep;
		} else {
			skipmode  = ctxtp->default_skipmode;
			skipdestp = ctxtp->default_skiplabelp;
			skipxferp = ctxtp->default_skipxferp;
		}

		switch (skipmode) {

		case IXL1394_SKIP_TO_SELF:
			/*
			 * mode is skip to self:
			 *   if context is stopped, return stopped, else
			 *   if dma location reg not enabled, return done
			 *   else, return location indeterminate
			 */
			if (HCI1394_ISOCH_CTXT_ACTIVE(soft_statep, ctxtp) ==
			    0) {
				TNF_PROBE_1_DEBUG(
					hci1394_ixl_intr_check_done_exit,
					HCI1394_TNF_HAL_STACK_ISOCH, "",
					tnf_string, msg, "CHECK_STOP");
				return (IXL_CHECK_STOP);
			}

			if (!dma_loc_check_enabled) {
				TNF_PROBE_1_DEBUG(
					hci1394_ixl_intr_check_done_exit,
					HCI1394_TNF_HAL_STACK_ISOCH, "",
					tnf_string, msg, "CHECK_DONE");
				return (IXL_CHECK_DONE);
			}

			TNF_PROBE_1_DEBUG(hci1394_ixl_intr_check_done_exit,
			    HCI1394_TNF_HAL_STACK_ISOCH, "", tnf_string, msg,
			    "CHECK_LOST");
			return (IXL_CHECK_LOST);

		case IXL1394_SKIP_TO_NEXT:
			/*
			 * mode is skip to next:
			 *    set potential skip target to current command at
			 *    next depth
			 */
			skipdestp = ixlp;
			skipxferp = ixlp;
			skipdepth = ixldepth + 1;

			/*
			 * else if at max depth at current cmd adjust to next
			 * IXL command.
			 *
			 * (NOTE: next means next IXL command along execution
			 * path,  whatever IXL command it might be.  e.g. store
			 * timestamp or callback or label or jump or send... )
			 */
			if (skipdepth >= xferctlp->cnt) {
				skipdepth = 0;
				skipdestp = ixlp->next_ixlp;
				skipxferp = xferctlp->execp;
			}

			/* evaluate skip to status further, below */
			break;


		case IXL1394_SKIP_TO_LABEL:
			/*
			 * mode is skip to label:
			 *    set skip destination depth to 0 (should be
			 *    redundant)
			 */
			skipdepth = 0;

			/* evaluate skip to status further, below */
			break;

		case IXL1394_SKIP_TO_STOP:
			/*
			 * mode is skip to stop:
			 *    set all xfer and destination skip to locations to
			 *    null
			 */
			skipxferp = NULL;
			skipdestp = NULL;
			skipdepth = 0;

			/* evaluate skip to status further, below */
			break;

		} /* end switch */

		/*
		 * if no xfer IXL command follows at or after current skip-to
		 * location
		 */
		if (skipxferp == NULL) {
			/*
			 *   if context is stopped, return stopped, else
			 *   if dma location reg not enabled, return done
			 *   else, return location indeterminate
			 */
			if (HCI1394_ISOCH_CTXT_ACTIVE(soft_statep, ctxtp) ==
			    0) {
				TNF_PROBE_1_DEBUG(
					hci1394_ixl_intr_check_done_exit,
					HCI1394_TNF_HAL_STACK_ISOCH, "",
					tnf_string, msg, "CHECK_STOP");
				return (IXL_CHECK_STOP);
			}

			if (!dma_loc_check_enabled) {
				TNF_PROBE_1_DEBUG(
					hci1394_ixl_intr_check_done_exit,
					HCI1394_TNF_HAL_STACK_ISOCH, "",
					tnf_string, msg, "CHECK_DONE");
				return (IXL_CHECK_DONE);
			}
			TNF_PROBE_1_DEBUG(hci1394_ixl_intr_check_done_exit,
			    HCI1394_TNF_HAL_STACK_ISOCH, "", tnf_string, msg,
			    "CHECK_LOST");
			return (IXL_CHECK_LOST);
		}

		/*
		 * if the skip to xfer IXL dma descriptor's status is set,
		 * then execution did skip
		 */
		xferctlp = (hci1394_xfer_ctl_t *)skipxferp->compiler_privatep;
		dma	 = &xferctlp->dma[skipdepth];
		acc_hdl  = dma->dma_buf->bi_handle;
		dma_hdl  = dma->dma_buf->bi_dma_handle;
		hcidescp = (hci1394_desc_t *)dma->dma_descp;
		hcidesc_off = (off_t)hcidescp - (off_t)dma->dma_buf->bi_kaddr;

		/* Sync the descriptor before we get the status */
		err = ddi_dma_sync(dma_hdl, hcidesc_off,
		    sizeof (hci1394_desc_t), DDI_DMA_SYNC_FORCPU);
		if (err != DDI_SUCCESS) {
			TNF_PROBE_1(hci1394_ixl_intr_check_done_error,
			    HCI1394_TNF_HAL_ERROR_ISOCH, "", tnf_string, errmsg,
			    "dma_sync() failed");
		}
		desc_status = ddi_get32(acc_hdl, &hcidescp->status);

		if ((desc_status & DESC_XFER_ACTIVE_MASK) != 0) {

			/*
			 * adjust to continue from skip to IXL command and
			 * return skipped, to have calling func continue.
			 * (Note: next IXL command may be any allowed IXL
			 * command)
			 */
			ctxtp->ixl_execp = skipdestp;
			ctxtp->ixl_exec_depth = skipdepth;

			TNF_PROBE_1_DEBUG(hci1394_ixl_intr_check_done_exit,
			    HCI1394_TNF_HAL_STACK_ISOCH, "", tnf_string, msg,
			    "CHECK_SKIP");
			return (IXL_CHECK_SKIP);
		}

		/*
		 * if dma location command register checking is enabled,
		 * and the skip to xfer IXL dma location matches current
		 * dma location register value, execution did skip
		 */
		dmastartp = dma->dma_bound & ~DESC_Z_MASK;
		dmaendp = dmastartp + ((dma->dma_bound & DESC_Z_MASK) << 4);

		if (dma_loc_check_enabled && ((dma_cmd_cur_loc >= dmastartp) &&
		    (dma_cmd_cur_loc < dmaendp))) {

			/* if the context is stopped, return stopped */
			if (HCI1394_ISOCH_CTXT_ACTIVE(soft_statep, ctxtp) ==
			    0) {
				TNF_PROBE_1_DEBUG(
					hci1394_ixl_intr_check_done_exit,
					HCI1394_TNF_HAL_STACK_ISOCH, "",
					tnf_string, msg, "CHECK STOP");
				return (IXL_CHECK_STOP);
			}
			/*
			 * adjust to continue from skip to IXL command and
			 * return skipped, to have calling func continue
			 * (Note: next IXL command may be any allowed IXL cmd)
			 */
			ctxtp->ixl_execp = skipdestp;
			ctxtp->ixl_exec_depth = skipdepth;

			TNF_PROBE_1_DEBUG(hci1394_ixl_intr_check_done_exit,
			    HCI1394_TNF_HAL_STACK_ISOCH, "", tnf_string, msg,
			    "CHECK_SKIP");
			return (IXL_CHECK_SKIP);
		}

		/*
		 * else, advance working current locn to skipxferp and
		 * skipdepth and continue skip evaluation loop processing
		 */
		ixlp = skipxferp;
		ixldepth = skipdepth;

	} /* end while */

	/*
	 * didn't find dma status set, nor location reg match, along skip path
	 *
	 * if context is stopped, return stopped,
	 *
	 * else if no current location reg active don't change context values,
	 * just return done (no skip)
	 *
	 * else, return location indeterminate
	 */

	if (HCI1394_ISOCH_CTXT_ACTIVE(soft_statep, ctxtp) == 0) {
		TNF_PROBE_1_DEBUG(hci1394_ixl_intr_check_done_exit,
		    HCI1394_TNF_HAL_STACK_ISOCH, "", tnf_string, msg,
		    "CHECK_STOP");
		return (IXL_CHECK_STOP);
	}
	if (!dma_loc_check_enabled) {
		TNF_PROBE_1_DEBUG(hci1394_ixl_intr_check_done_exit,
		    HCI1394_TNF_HAL_STACK_ISOCH, "", tnf_string, msg,
		    "CHECK_DONE");
		return (IXL_CHECK_DONE);
	}

	TNF_PROBE_1_DEBUG(hci1394_ixl_intr_check_done_exit,
	    HCI1394_TNF_HAL_STACK_ISOCH, "", tnf_string, msg, "CHECK_LOST");
	return (IXL_CHECK_LOST);
}