/* 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); }
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); }
/* * 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); }
/* 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); }
/*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"); } } }
/* * 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 */
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 */
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); } }
/* * 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); }
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); }
/* * 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); }
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 {
/* * 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); }
/* * 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 */
/* * 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; }
/* * 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); }