/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * Copyright (c) 2002-2006 Neterion, Inc. */ #ifdef XGE_DEBUG_FP #include "xgehal-fifo.h" #endif __HAL_STATIC_FIFO __HAL_INLINE_FIFO xge_hal_fifo_txdl_priv_t* __hal_fifo_txdl_priv(xge_hal_dtr_h dtrh) { xge_hal_fifo_txd_t *txdp = (xge_hal_fifo_txd_t*)dtrh; xge_hal_fifo_txdl_priv_t *txdl_priv; xge_assert(txdp); txdl_priv = (xge_hal_fifo_txdl_priv_t *) (ulong_t)txdp->host_control; xge_assert(txdl_priv); xge_assert(txdl_priv->dma_object); xge_assert(txdl_priv->dma_addr); xge_assert(txdl_priv->dma_object->handle == txdl_priv->dma_handle); return txdl_priv; } __HAL_STATIC_FIFO __HAL_INLINE_FIFO void __hal_fifo_dtr_post_single(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh, u64 ctrl_1) { xge_hal_fifo_t *fifo = (xge_hal_fifo_t *)channelh; xge_hal_fifo_hw_pair_t *hw_pair = fifo->hw_pair; xge_hal_fifo_txd_t *txdp = (xge_hal_fifo_txd_t *)dtrh; xge_hal_fifo_txdl_priv_t *txdl_priv; u64 ctrl; txdp->control_1 |= XGE_HAL_TXD_LIST_OWN_XENA; #ifdef XGE_DEBUG_ASSERT /* make sure Xena overwrites the (illegal) t_code value on completion */ XGE_HAL_SET_TXD_T_CODE(txdp->control_1, XGE_HAL_TXD_T_CODE_UNUSED_5); #endif txdl_priv = __hal_fifo_txdl_priv(dtrh); #if defined(XGE_OS_DMA_REQUIRES_SYNC) && defined(XGE_HAL_DMA_DTR_STREAMING) /* sync the TxDL to device */ xge_os_dma_sync(fifo->channel.pdev, txdl_priv->dma_handle, txdl_priv->dma_addr, txdl_priv->dma_offset, txdl_priv->frags << 5 /* sizeof(xge_hal_fifo_txd_t) */, XGE_OS_DMA_DIR_TODEVICE); #endif /* write the pointer first */ xge_os_pio_mem_write64(fifo->channel.pdev, fifo->channel.regh1, txdl_priv->dma_addr, &hw_pair->txdl_pointer); /* spec: 0x00 = 1 TxD in the list */ ctrl = XGE_HAL_TX_FIFO_LAST_TXD_NUM(txdl_priv->frags - 1); ctrl |= ctrl_1; ctrl |= fifo->no_snoop_bits; if (txdp->control_1 & XGE_HAL_TXD_LSO_COF_CTRL(XGE_HAL_TXD_TCP_LSO)) { ctrl |= XGE_HAL_TX_FIFO_SPECIAL_FUNC; } /* * according to the XENA spec: * * It is important to note that pointers and list control words are * always written in pairs: in the first write, the host must write a * pointer, and in the second write, it must write the list control * word. Any other access will result in an error. Also, all 16 bytes * of the pointer/control structure must be written, including any * reserved bytes. */ xge_os_wmb(); /* * we want touch work_arr in order with ownership bit set to HW */ __hal_channel_dtr_post(channelh, dtrh); xge_os_pio_mem_write64(fifo->channel.pdev, fifo->channel.regh1, ctrl, &hw_pair->list_control); xge_debug_fifo(XGE_TRACE, "posted txdl 0x"XGE_OS_LLXFMT" ctrl 0x"XGE_OS_LLXFMT" " "into 0x"XGE_OS_LLXFMT"", (unsigned long long)txdl_priv->dma_addr, (unsigned long long)ctrl, (unsigned long long)(ulong_t)&hw_pair->txdl_pointer); #ifdef XGE_HAL_FIFO_DUMP_TXD xge_os_printf(""XGE_OS_LLXFMT":"XGE_OS_LLXFMT":"XGE_OS_LLXFMT":" XGE_OS_LLXFMT" dma "XGE_OS_LLXFMT, txdp->control_1, txdp->control_2, txdp->buffer_pointer, txdp->host_control, txdl_priv->dma_addr); #endif fifo->channel.stats.total_posts++; fifo->channel.usage_cnt++; if (fifo->channel.stats.usage_max < fifo->channel.usage_cnt) fifo->channel.stats.usage_max = fifo->channel.usage_cnt; } __HAL_STATIC_FIFO __HAL_INLINE_FIFO void __hal_fifo_txdl_free_many(xge_hal_channel_h channelh, xge_hal_fifo_txd_t *txdp, int list_size, int frags) { xge_hal_fifo_txdl_priv_t *current_txdl_priv; xge_hal_fifo_txdl_priv_t *next_txdl_priv; int invalid_frags = frags % list_size; if (invalid_frags){ xge_debug_fifo(XGE_ERR, "freeing corrupt dtrh %p, fragments %d list size %d", txdp, frags, list_size); xge_assert(invalid_frags == 0); } while(txdp){ xge_debug_fifo(XGE_TRACE, "freeing linked dtrh %p, fragments %d list size %d", txdp, frags, list_size); current_txdl_priv = __hal_fifo_txdl_priv(txdp); #if defined(XGE_DEBUG_ASSERT) && defined(XGE_OS_MEMORY_CHECK) current_txdl_priv->allocated = 0; #endif __hal_channel_dtr_free(channelh, txdp); next_txdl_priv = current_txdl_priv->next_txdl_priv; xge_assert(frags); frags -= list_size; if (next_txdl_priv) { current_txdl_priv->next_txdl_priv = NULL; txdp = next_txdl_priv->first_txdp; } else { xge_debug_fifo(XGE_TRACE, "freed linked dtrh fragments %d list size %d", frags, list_size); break; } } xge_assert(frags == 0) } __HAL_STATIC_FIFO __HAL_INLINE_FIFO void __hal_fifo_txdl_restore_many(xge_hal_channel_h channelh, xge_hal_fifo_txd_t *txdp, int txdl_count) { xge_hal_fifo_txdl_priv_t *current_txdl_priv; xge_hal_fifo_txdl_priv_t *next_txdl_priv; int i = txdl_count; xge_assert(((xge_hal_channel_t *)channelh)->reserve_length + txdl_count <= ((xge_hal_channel_t *)channelh)->reserve_initial); current_txdl_priv = __hal_fifo_txdl_priv(txdp); do{ xge_assert(i); #if defined(XGE_DEBUG_ASSERT) && defined(XGE_OS_MEMORY_CHECK) current_txdl_priv->allocated = 0; #endif next_txdl_priv = current_txdl_priv->next_txdl_priv; txdp = current_txdl_priv->first_txdp; current_txdl_priv->next_txdl_priv = NULL; __hal_channel_dtr_restore(channelh, (xge_hal_dtr_h )txdp, --i); xge_debug_fifo(XGE_TRACE, "dtrh %p restored at offset %d", txdp, i); current_txdl_priv = next_txdl_priv; } while(current_txdl_priv); __hal_channel_dtr_restore(channelh, NULL, txdl_count); } /** * xge_hal_fifo_dtr_private - Retrieve per-descriptor private data. * @channelh: Channel handle. * @dtrh: Descriptor handle. * * Retrieve per-descriptor private data. * Note that ULD requests per-descriptor space via * xge_hal_channel_open(). * * Returns: private ULD data associated with the descriptor. * Usage: See ex_xmit{} and ex_tx_compl{}. */ __HAL_STATIC_FIFO __HAL_INLINE_FIFO void* xge_hal_fifo_dtr_private(xge_hal_dtr_h dtrh) { xge_hal_fifo_txd_t *txdp = (xge_hal_fifo_txd_t *)dtrh; return ((char *)(ulong_t)txdp->host_control) + sizeof(xge_hal_fifo_txdl_priv_t); } /** * xge_hal_fifo_dtr_buffer_cnt - Get number of buffers carried by the * descriptor. * @dtrh: Descriptor handle. * * Returns: Number of buffers stored in the given descriptor. Can be used * _after_ the descriptor is set up for posting (see * xge_hal_fifo_dtr_post()) and _before_ it is deallocated (see * xge_hal_fifo_dtr_free()). * */ __HAL_STATIC_FIFO __HAL_INLINE_FIFO int xge_hal_fifo_dtr_buffer_cnt(xge_hal_dtr_h dtrh) { xge_hal_fifo_txdl_priv_t *txdl_priv; txdl_priv = __hal_fifo_txdl_priv(dtrh); return txdl_priv->frags; } /** * xge_hal_fifo_dtr_reserve_many- Reserve fifo descriptors which span more * than single txdl. * @channelh: Channel handle. * @dtrh: Reserved descriptor. On success HAL fills this "out" parameter * with a valid handle. * @frags: minimum number of fragments to be reserved. * * Reserve TxDL(s) (that is, fifo descriptor) * for the subsequent filling-in by upper layerdriver (ULD)) * and posting on the corresponding channel (@channelh) * via xge_hal_fifo_dtr_post(). * * Returns: XGE_HAL_OK - success; * XGE_HAL_INF_OUT_OF_DESCRIPTORS - Currently no descriptors available * * See also: xge_hal_fifo_dtr_reserve_sp(), xge_hal_fifo_dtr_free(), * xge_hal_ring_dtr_reserve(), xge_hal_status_e{}. * Usage: See ex_xmit{}. */ __HAL_STATIC_FIFO __HAL_INLINE_FIFO xge_hal_status_e xge_hal_fifo_dtr_reserve_many(xge_hal_channel_h channelh, xge_hal_dtr_h *dtrh, const int frags) { xge_hal_status_e status = XGE_HAL_OK; int alloc_frags = 0, dang_frags = 0; xge_hal_fifo_txd_t *curr_txdp = NULL; xge_hal_fifo_txd_t *next_txdp; xge_hal_fifo_txdl_priv_t *next_txdl_priv, *curr_txdl_priv = NULL; xge_hal_fifo_t *fifo = (xge_hal_fifo_t *)channelh; int max_frags = fifo->config->max_frags; xge_hal_dtr_h dang_dtrh = NULL; #if defined(XGE_HAL_TX_MULTI_RESERVE_IRQ) unsigned long flags=0; #endif xge_debug_fifo(XGE_TRACE, "dtr_reserve_many called for frags %d", frags); xge_assert(frags < (fifo->txdl_per_memblock * max_frags)); #if defined(XGE_HAL_TX_MULTI_RESERVE) xge_os_spin_lock(&fifo->channel.reserve_lock); #elif defined(XGE_HAL_TX_MULTI_RESERVE_IRQ) xge_os_spin_lock_irq(&fifo->channel.reserve_lock, flags); #endif while(alloc_frags < frags) { status = __hal_channel_dtr_alloc(channelh, (xge_hal_dtr_h *)(void*)&next_txdp); if (status != XGE_HAL_OK){ xge_debug_fifo(XGE_ERR, "failed to allocate linked fragments rc %d", status); xge_assert(status == XGE_HAL_INF_OUT_OF_DESCRIPTORS); if (*dtrh) { xge_assert(alloc_frags/max_frags); __hal_fifo_txdl_restore_many(channelh, (xge_hal_fifo_txd_t *) *dtrh, alloc_frags/max_frags); } if (dang_dtrh) { xge_assert(dang_frags/max_frags); __hal_fifo_txdl_restore_many(channelh, (xge_hal_fifo_txd_t *) dang_dtrh, dang_frags/max_frags); } break; } xge_debug_fifo(XGE_TRACE, "allocated linked dtrh %p" " for frags %d", next_txdp, frags); next_txdl_priv = __hal_fifo_txdl_priv(next_txdp); xge_assert(next_txdl_priv); xge_assert(next_txdl_priv->first_txdp == next_txdp); next_txdl_priv->dang_txdl = NULL; next_txdl_priv->dang_frags = 0; next_txdl_priv->next_txdl_priv = NULL; #if defined(XGE_OS_MEMORY_CHECK) next_txdl_priv->allocated = 1; #endif if (!curr_txdp || !curr_txdl_priv) { curr_txdp = next_txdp; curr_txdl_priv = next_txdl_priv; *dtrh = (xge_hal_dtr_h)next_txdp; alloc_frags = max_frags; continue; } if (curr_txdl_priv->memblock == next_txdl_priv->memblock) { xge_debug_fifo(XGE_TRACE, "linking dtrh %p, with %p", *dtrh, next_txdp); xge_assert (next_txdp == curr_txdp + max_frags); alloc_frags += max_frags; curr_txdl_priv->next_txdl_priv = next_txdl_priv; } else { xge_assert(*dtrh); xge_assert(dang_dtrh == NULL); dang_dtrh = *dtrh; dang_frags = alloc_frags; xge_debug_fifo(XGE_TRACE, "dangling dtrh %p, linked with dtrh %p", *dtrh, next_txdp); next_txdl_priv->dang_txdl = (xge_hal_fifo_txd_t *) *dtrh; next_txdl_priv->dang_frags = alloc_frags; alloc_frags = max_frags; *dtrh = next_txdp; } curr_txdp = next_txdp; curr_txdl_priv = next_txdl_priv; } #if defined(XGE_HAL_TX_MULTI_RESERVE) xge_os_spin_unlock(&fifo->channel.reserve_lock); #elif defined(XGE_HAL_TX_MULTI_RESERVE_IRQ) xge_os_spin_unlock_irq(&fifo->channel.reserve_lock, flags); #endif if (status == XGE_HAL_OK) { xge_hal_fifo_txdl_priv_t * txdl_priv; xge_hal_fifo_txd_t *txdp = (xge_hal_fifo_txd_t *)*dtrh; xge_hal_stats_channel_info_t *statsp = &fifo->channel.stats; txdl_priv = __hal_fifo_txdl_priv(txdp); /* reset the TxDL's private */ txdl_priv->align_dma_offset = 0; txdl_priv->align_vaddr_start = txdl_priv->align_vaddr; txdl_priv->align_used_frags = 0; txdl_priv->frags = 0; txdl_priv->bytes_sent = 0; txdl_priv->alloc_frags = alloc_frags; /* reset TxD0 */ txdp->control_1 = txdp->control_2 = 0; #if defined(XGE_OS_MEMORY_CHECK) txdl_priv->allocated = 1; #endif /* update statistics */ statsp->total_posts_dtrs_many++; statsp->total_posts_frags_many += txdl_priv->alloc_frags; if (txdl_priv->dang_frags){ statsp->total_posts_dang_dtrs++; statsp->total_posts_dang_frags += txdl_priv->dang_frags; } } return status; } /** * xge_hal_fifo_dtr_reserve - Reserve fifo descriptor. * @channelh: Channel handle. * @dtrh: Reserved descriptor. On success HAL fills this "out" parameter * with a valid handle. * * Reserve a single TxDL (that is, fifo descriptor) * for the subsequent filling-in by upper layerdriver (ULD)) * and posting on the corresponding channel (@channelh) * via xge_hal_fifo_dtr_post(). * * Note: it is the responsibility of ULD to reserve multiple descriptors * for lengthy (e.g., LSO) transmit operation. A single fifo descriptor * carries up to configured number (fifo.max_frags) of contiguous buffers. * * Returns: XGE_HAL_OK - success; * XGE_HAL_INF_OUT_OF_DESCRIPTORS - Currently no descriptors available * * See also: xge_hal_fifo_dtr_reserve_sp(), xge_hal_fifo_dtr_free(), * xge_hal_ring_dtr_reserve(), xge_hal_status_e{}. * Usage: See ex_xmit{}. */ __HAL_STATIC_FIFO __HAL_INLINE_FIFO xge_hal_status_e xge_hal_fifo_dtr_reserve(xge_hal_channel_h channelh, xge_hal_dtr_h *dtrh) { xge_hal_status_e status; #if defined(XGE_HAL_TX_MULTI_RESERVE_IRQ) unsigned long flags=0; #endif #if defined(XGE_HAL_TX_MULTI_RESERVE) xge_os_spin_lock(&((xge_hal_channel_t*)channelh)->reserve_lock); #elif defined(XGE_HAL_TX_MULTI_RESERVE_IRQ) xge_os_spin_lock_irq(&((xge_hal_channel_t*)channelh)->reserve_lock, flags); #endif status = __hal_channel_dtr_alloc(channelh, dtrh); #if defined(XGE_HAL_TX_MULTI_RESERVE) xge_os_spin_unlock(&((xge_hal_channel_t*)channelh)->reserve_lock); #elif defined(XGE_HAL_TX_MULTI_RESERVE_IRQ) xge_os_spin_unlock_irq(&((xge_hal_channel_t*)channelh)->reserve_lock, flags); #endif if (status == XGE_HAL_OK) { xge_hal_fifo_txd_t *txdp = (xge_hal_fifo_txd_t *)*dtrh; xge_hal_fifo_txdl_priv_t *txdl_priv; txdl_priv = __hal_fifo_txdl_priv(txdp); /* reset the TxDL's private */ txdl_priv->align_dma_offset = 0; txdl_priv->align_vaddr_start = txdl_priv->align_vaddr; txdl_priv->align_used_frags = 0; txdl_priv->frags = 0; txdl_priv->alloc_frags = ((xge_hal_fifo_t *)channelh)->config->max_frags; txdl_priv->dang_txdl = NULL; txdl_priv->dang_frags = 0; txdl_priv->next_txdl_priv = NULL; txdl_priv->bytes_sent = 0; /* reset TxD0 */ txdp->control_1 = txdp->control_2 = 0; #if defined(XGE_OS_MEMORY_CHECK) txdl_priv->allocated = 1; #endif } return status; } /** * xge_hal_fifo_dtr_reserve_sp - Reserve fifo descriptor and store it in * the ULD-provided "scratch" memory. * @channelh: Channel handle. * @dtr_sp_size: Size of the %dtr_sp "scratch pad" that HAL can use for TxDL. * @dtr_sp: "Scratch pad" supplied by upper-layer driver (ULD). * * Reserve TxDL and fill-in ULD supplied "scratch pad". The difference * between this API and xge_hal_fifo_dtr_reserve() is (possibly) - * performance. * * If upper-layer uses ULP-defined commands, and if those commands have enough * space for HAL/Xframe descriptors - tnan it is better (read: faster) to fit * all the per-command information into one command, which is typically * one contiguous block. * * Note: Unlike xge_hal_fifo_dtr_reserve(), this function can be used to * allocate a single descriptor for transmit operation. * * See also: xge_hal_fifo_dtr_reserve(), xge_hal_fifo_dtr_free(), * xge_hal_ring_dtr_reserve(), xge_hal_status_e{}. */ __HAL_STATIC_FIFO __HAL_INLINE_FIFO xge_hal_status_e xge_hal_fifo_dtr_reserve_sp(xge_hal_channel_h channelh, int dtr_sp_size, xge_hal_dtr_h dtr_sp) { /* FIXME: implement */ return XGE_HAL_OK; } /** * xge_hal_fifo_dtr_post - Post descriptor on the fifo channel. * @channelh: Channel handle. * @dtrh: Descriptor obtained via xge_hal_fifo_dtr_reserve() or * xge_hal_fifo_dtr_reserve_sp() * @frags: Number of contiguous buffers that are part of a single * transmit operation. * * Post descriptor on the 'fifo' type channel for transmission. * Prior to posting the descriptor should be filled in accordance with * Host/Xframe interface specification for a given service (LL, etc.). * * See also: xge_hal_fifo_dtr_post_many(), xge_hal_ring_dtr_post(). * Usage: See ex_xmit{}. */ __HAL_STATIC_FIFO __HAL_INLINE_FIFO void xge_hal_fifo_dtr_post(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh) { xge_hal_fifo_t *fifo = (xge_hal_fifo_t *)channelh; xge_hal_fifo_txdl_priv_t *txdl_priv; xge_hal_fifo_txd_t *txdp_last; xge_hal_fifo_txd_t *txdp_first; #if defined(XGE_HAL_TX_MULTI_POST_IRQ) unsigned long flags = 0; #endif txdl_priv = __hal_fifo_txdl_priv(dtrh); txdp_first = (xge_hal_fifo_txd_t *)dtrh; txdp_first->control_1 |= XGE_HAL_TXD_GATHER_CODE_FIRST; txdp_first->control_2 |= fifo->interrupt_type; txdp_last = (xge_hal_fifo_txd_t *)dtrh + (txdl_priv->frags - 1); txdp_last->control_1 |= XGE_HAL_TXD_GATHER_CODE_LAST; #if defined(XGE_HAL_TX_MULTI_POST) xge_os_spin_lock(fifo->post_lock_ptr); #elif defined(XGE_HAL_TX_MULTI_POST_IRQ) xge_os_spin_lock_irq(fifo->post_lock_ptr, flags); #endif __hal_fifo_dtr_post_single(channelh, dtrh, (u64)(XGE_HAL_TX_FIFO_FIRST_LIST | XGE_HAL_TX_FIFO_LAST_LIST)); #if defined(XGE_HAL_TX_MULTI_POST) xge_os_spin_unlock(fifo->post_lock_ptr); #elif defined(XGE_HAL_TX_MULTI_POST_IRQ) xge_os_spin_unlock_irq(fifo->post_lock_ptr, flags); #endif } /** * xge_hal_fifo_dtr_post_many - Post multiple descriptors on fifo * channel. * @channelh: Channel to post descriptor. * @num: Number of descriptors (i.e., fifo TxDLs) in the %dtrs[]. * @dtrs: Descriptors obtained via xge_hal_fifo_dtr_reserve(). * @frags_arr: Number of fragments carried @dtrs descriptors. * Note that frag_arr[i] corresponds to descriptor dtrs[i]. * * Post multi-descriptor on the fifo channel. The operation is atomic: * all descriptrs are posted on the channel "back-to-back' without * letting other posts (possibly driven by multiple transmitting threads) * to interleave. * * See also: xge_hal_fifo_dtr_post(), xge_hal_ring_dtr_post(). */ __HAL_STATIC_FIFO __HAL_INLINE_FIFO void xge_hal_fifo_dtr_post_many(xge_hal_channel_h channelh, int num, xge_hal_dtr_h dtrs[]) { int i; xge_hal_fifo_t *fifo = (xge_hal_fifo_t *)channelh; xge_hal_fifo_txd_t *txdp_last; xge_hal_fifo_txd_t *txdp_first; xge_hal_fifo_txdl_priv_t *txdl_priv_last; #if defined(XGE_HAL_TX_MULTI_POST_IRQ) unsigned long flags = 0; #endif xge_assert(num > 1); txdp_first = (xge_hal_fifo_txd_t *)dtrs[0]; txdp_first->control_1 |= XGE_HAL_TXD_GATHER_CODE_FIRST; txdp_first->control_2 |= fifo->interrupt_type; txdl_priv_last = __hal_fifo_txdl_priv(dtrs[num-1]); txdp_last = (xge_hal_fifo_txd_t *)dtrs[num-1] + (txdl_priv_last->frags - 1); txdp_last->control_1 |= XGE_HAL_TXD_GATHER_CODE_LAST; #if defined(XGE_HAL_TX_MULTI_POST) xge_os_spin_lock(&((xge_hal_channel_t*)channelh)->post_lock); #elif defined(XGE_HAL_TX_MULTI_POST_IRQ) xge_os_spin_lock_irq(&((xge_hal_channel_t*)channelh)->post_lock, flags); #endif for (i=0; ipost_lock); #elif defined(XGE_HAL_TX_MULTI_POST_IRQ) xge_os_spin_unlock_irq(&((xge_hal_channel_t*)channelh)->post_lock, flags); #endif fifo->channel.stats.total_posts_many++; } /** * xge_hal_fifo_dtr_next_completed - Retrieve next completed descriptor. * @channelh: Channel handle. * @dtrh: Descriptor handle. Returned by HAL. * @t_code: Transfer code, as per Xframe User Guide, * Transmit Descriptor Format. * Returned by HAL. * * Retrieve the _next_ completed descriptor. * HAL uses channel callback (*xge_hal_channel_callback_f) to notifiy * upper-layer driver (ULD) of new completed descriptors. After that * the ULD can use xge_hal_fifo_dtr_next_completed to retrieve the rest * completions (the very first completion is passed by HAL via * xge_hal_channel_callback_f). * * Implementation-wise, the upper-layer driver is free to call * xge_hal_fifo_dtr_next_completed either immediately from inside the * channel callback, or in a deferred fashion and separate (from HAL) * context. * * Non-zero @t_code means failure to process the descriptor. * The failure could happen, for instance, when the link is * down, in which case Xframe completes the descriptor because it * is not able to send the data out. * * For details please refer to Xframe User Guide. * * Returns: XGE_HAL_OK - success. * XGE_HAL_INF_NO_MORE_COMPLETED_DESCRIPTORS - No completed descriptors * are currently available for processing. * * See also: xge_hal_channel_callback_f{}, * xge_hal_ring_dtr_next_completed(). * Usage: See ex_tx_compl{}. */ __HAL_STATIC_FIFO __HAL_INLINE_FIFO xge_hal_status_e xge_hal_fifo_dtr_next_completed(xge_hal_channel_h channelh, xge_hal_dtr_h *dtrh, u8 *t_code) { xge_hal_fifo_txd_t *txdp; xge_hal_fifo_t *fifo = (xge_hal_fifo_t *)channelh; #if defined(XGE_OS_DMA_REQUIRES_SYNC) && defined(XGE_HAL_DMA_DTR_STREAMING) xge_hal_fifo_txdl_priv_t *txdl_priv; #endif __hal_channel_dtr_try_complete(channelh, dtrh); txdp = (xge_hal_fifo_txd_t *)*dtrh; if (txdp == NULL) { return XGE_HAL_INF_NO_MORE_COMPLETED_DESCRIPTORS; } #if defined(XGE_OS_DMA_REQUIRES_SYNC) && defined(XGE_HAL_DMA_DTR_STREAMING) txdl_priv = __hal_fifo_txdl_priv(txdp); /* sync TxDL to read the ownership * * Note: 16bytes means Control_1 & Control_2 */ xge_os_dma_sync(fifo->channel.pdev, txdl_priv->dma_handle, txdl_priv->dma_addr, txdl_priv->dma_offset, 16, XGE_OS_DMA_DIR_FROMDEVICE); #endif /* check whether host owns it */ if ( !(txdp->control_1 & XGE_HAL_TXD_LIST_OWN_XENA) ) { xge_assert(txdp->host_control!=0); __hal_channel_dtr_complete(channelh); *t_code = (u8)XGE_HAL_GET_TXD_T_CODE(txdp->control_1); /* see XGE_HAL_SET_TXD_T_CODE() above.. */ xge_assert(*t_code != XGE_HAL_TXD_T_CODE_UNUSED_5); if (fifo->channel.usage_cnt > 0) fifo->channel.usage_cnt--; return XGE_HAL_OK; } /* no more completions */ *dtrh = 0; return XGE_HAL_INF_NO_MORE_COMPLETED_DESCRIPTORS; } /** * xge_hal_fifo_dtr_free - Free descriptor. * @channelh: Channel handle. * @dtr: Descriptor handle. * * Free the reserved descriptor. This operation is "symmetrical" to * xge_hal_fifo_dtr_reserve or xge_hal_fifo_dtr_reserve_sp. * The "free-ing" completes the descriptor's lifecycle. * * After free-ing (see xge_hal_fifo_dtr_free()) the descriptor again can * be: * * - reserved (xge_hal_fifo_dtr_reserve); * * - posted (xge_hal_fifo_dtr_post); * * - completed (xge_hal_fifo_dtr_next_completed); * * - and recycled again (xge_hal_fifo_dtr_free). * * For alternative state transitions and more details please refer to * the design doc. * * See also: xge_hal_ring_dtr_free(), xge_hal_fifo_dtr_reserve(). * Usage: See ex_tx_compl{}. */ __HAL_STATIC_FIFO __HAL_INLINE_FIFO void xge_hal_fifo_dtr_free(xge_hal_channel_h channelh, xge_hal_dtr_h dtr) { #if defined(XGE_HAL_TX_MULTI_FREE_IRQ) unsigned long flags = 0; #endif xge_hal_fifo_txdl_priv_t *txdl_priv = __hal_fifo_txdl_priv( (xge_hal_fifo_txd_t *)dtr); int max_frags = ((xge_hal_fifo_t *)channelh)->config->max_frags; #if defined(XGE_HAL_TX_MULTI_FREE) xge_os_spin_lock(&((xge_hal_channel_t*)channelh)->free_lock); #elif defined(XGE_HAL_TX_MULTI_FREE_IRQ) xge_os_spin_lock_irq(&((xge_hal_channel_t*)channelh)->free_lock, flags); #endif if (txdl_priv->alloc_frags > max_frags) { xge_hal_fifo_txd_t *dang_txdp = (xge_hal_fifo_txd_t *) txdl_priv->dang_txdl; int dang_frags = txdl_priv->dang_frags; int alloc_frags = txdl_priv->alloc_frags; txdl_priv->dang_txdl = NULL; txdl_priv->dang_frags = 0; txdl_priv->alloc_frags = 0; /* dtrh must have a linked list of dtrh */ xge_assert(txdl_priv->next_txdl_priv); /* free any dangling dtrh first */ if (dang_txdp) { xge_debug_fifo(XGE_TRACE, "freeing dangled dtrh %p for %d fragments", dang_txdp, dang_frags); __hal_fifo_txdl_free_many(channelh, dang_txdp, max_frags, dang_frags); } /* now free the reserved dtrh list */ xge_debug_fifo(XGE_TRACE, "freeing dtrh %p list of %d fragments", dtr, alloc_frags); __hal_fifo_txdl_free_many(channelh, (xge_hal_fifo_txd_t *)dtr, max_frags, alloc_frags); } else __hal_channel_dtr_free(channelh, dtr); ((xge_hal_channel_t *)channelh)->poll_bytes += txdl_priv->bytes_sent; #if defined(XGE_DEBUG_ASSERT) && defined(XGE_OS_MEMORY_CHECK) __hal_fifo_txdl_priv(dtr)->allocated = 0; #endif #if defined(XGE_HAL_TX_MULTI_FREE) xge_os_spin_unlock(&((xge_hal_channel_t*)channelh)->free_lock); #elif defined(XGE_HAL_TX_MULTI_FREE_IRQ) xge_os_spin_unlock_irq(&((xge_hal_channel_t*)channelh)->free_lock, flags); #endif } /** * xge_hal_fifo_dtr_buffer_set_aligned - Align transmit buffer and fill * in fifo descriptor. * @channelh: Channel handle. * @dtrh: Descriptor handle. * @frag_idx: Index of the data buffer in the caller's scatter-gather listá * (of buffers). * @vaddr: Virtual address of the data buffer. * @dma_pointer: DMA address of the data buffer referenced by @frag_idx. * @size: Size of the data buffer (in bytes). * @misaligned_size: Size (in bytes) of the misaligned portion of the * data buffer. Calculated by the caller, based on the platform/OS/other * specific criteria, which is outside of HAL's domain. See notes below. * * This API is part of the transmit descriptor preparation for posting * (via xge_hal_fifo_dtr_post()). The related "preparation" APIs include * xge_hal_fifo_dtr_mss_set() and xge_hal_fifo_dtr_cksum_set_bits(). * All three APIs fill in the fields of the fifo descriptor, * in accordance with the Xframe specification. * On the PCI-X based systems aligning transmit data typically provides better * transmit performance. The typical alignment granularity: L2 cacheline size. * However, HAL does not make assumptions in terms of the alignment granularity; * this is specified via additional @misaligned_size parameter described above. * Prior to calling xge_hal_fifo_dtr_buffer_set_aligned(), * ULD is supposed to check alignment of a given fragment/buffer. For this HAL * provides a separate xge_hal_check_alignment() API sufficient to cover * most (but not all) possible alignment criteria. * If the buffer appears to be aligned, the ULD calls * xge_hal_fifo_dtr_buffer_set(). * Otherwise, ULD calls xge_hal_fifo_dtr_buffer_set_aligned(). * * Note; This API is a "superset" of xge_hal_fifo_dtr_buffer_set(). In * addition to filling in the specified descriptor it aligns transmit data on * the specified boundary. * Note: Decision on whether to align or not to align a given contiguous * transmit buffer is outside of HAL's domain. To this end ULD can use any * programmable criteria, which can help to 1) boost transmit performance, * and/or 2) provide a workaround for PCI bridge bugs, if any. * * See also: xge_hal_fifo_dtr_buffer_set(), * xge_hal_check_alignment(). * * See also: xge_hal_fifo_dtr_reserve(), xge_hal_fifo_dtr_post(), * xge_hal_fifo_dtr_mss_set(), xge_hal_fifo_dtr_cksum_set_bits() */ __HAL_STATIC_FIFO __HAL_INLINE_FIFO xge_hal_status_e xge_hal_fifo_dtr_buffer_set_aligned(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh, int frag_idx, void *vaddr, dma_addr_t dma_pointer, int size, int misaligned_size) { xge_hal_fifo_t *fifo = (xge_hal_fifo_t *)channelh; xge_hal_fifo_txdl_priv_t *txdl_priv; xge_hal_fifo_txd_t *txdp; int remaining_size; ptrdiff_t prev_boff; txdl_priv = __hal_fifo_txdl_priv(dtrh); txdp = (xge_hal_fifo_txd_t *)dtrh + txdl_priv->frags; if (frag_idx != 0) { txdp->control_1 = txdp->control_2 = 0; } /* On some systems buffer size could be zero. * It is the responsibility of ULD and *not HAL* to * detect it and skip it. */ xge_assert(size > 0); xge_assert(frag_idx < txdl_priv->alloc_frags); xge_assert(misaligned_size != 0 && misaligned_size <= fifo->config->alignment_size); remaining_size = size - misaligned_size; xge_assert(remaining_size >= 0); xge_os_memcpy((char*)txdl_priv->align_vaddr_start, vaddr, misaligned_size); if (txdl_priv->align_used_frags >= fifo->config->max_aligned_frags) { return XGE_HAL_ERR_OUT_ALIGNED_FRAGS; } /* setup new buffer */ prev_boff = txdl_priv->align_vaddr_start - txdl_priv->align_vaddr; txdp->buffer_pointer = (u64)txdl_priv->align_dma_addr + prev_boff; txdp->control_1 |= XGE_HAL_TXD_BUFFER0_SIZE(misaligned_size); txdl_priv->bytes_sent += misaligned_size; fifo->channel.stats.total_buffers++; txdl_priv->frags++; txdl_priv->align_used_frags++; txdl_priv->align_vaddr_start += fifo->config->alignment_size; txdl_priv->align_dma_offset = 0; #if defined(XGE_OS_DMA_REQUIRES_SYNC) /* sync new buffer */ xge_os_dma_sync(fifo->channel.pdev, txdl_priv->align_dma_handle, txdp->buffer_pointer, 0, misaligned_size, XGE_OS_DMA_DIR_TODEVICE); #endif if (remaining_size) { xge_assert(frag_idx < txdl_priv->alloc_frags); txdp++; txdp->buffer_pointer = (u64)dma_pointer + misaligned_size; txdp->control_1 = XGE_HAL_TXD_BUFFER0_SIZE(remaining_size); txdl_priv->bytes_sent += remaining_size; txdp->control_2 = 0; fifo->channel.stats.total_buffers++; txdl_priv->frags++; } return XGE_HAL_OK; } /** * xge_hal_fifo_dtr_buffer_append - Append the contents of virtually * contiguous data buffer to a single physically contiguous buffer. * @channelh: Channel handle. * @dtrh: Descriptor handle. * @vaddr: Virtual address of the data buffer. * @size: Size of the data buffer (in bytes). * * This API is part of the transmit descriptor preparation for posting * (via xge_hal_fifo_dtr_post()). * The main difference of this API wrt to the APIs * xge_hal_fifo_dtr_buffer_set_aligned() is that this API appends the * contents of virtually contiguous data buffers received from * upper layer into a single physically contiguous data buffer and the * device will do a DMA from this buffer. * * See Also: xge_hal_fifo_dtr_buffer_finalize(), xge_hal_fifo_dtr_buffer_set(), * xge_hal_fifo_dtr_buffer_set_aligned(). */ __HAL_STATIC_FIFO __HAL_INLINE_FIFO xge_hal_status_e xge_hal_fifo_dtr_buffer_append(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh, void *vaddr, int size) { xge_hal_fifo_t *fifo = (xge_hal_fifo_t *)channelh; xge_hal_fifo_txdl_priv_t *txdl_priv; ptrdiff_t used; xge_assert(size > 0); txdl_priv = __hal_fifo_txdl_priv(dtrh); used = txdl_priv->align_vaddr_start - txdl_priv->align_vaddr; used += txdl_priv->align_dma_offset; if (used + (unsigned int)size > (unsigned int)fifo->align_size) return XGE_HAL_ERR_OUT_ALIGNED_FRAGS; xge_os_memcpy((char*)txdl_priv->align_vaddr_start + txdl_priv->align_dma_offset, vaddr, size); fifo->channel.stats.copied_frags++; txdl_priv->align_dma_offset += size; return XGE_HAL_OK; } /** * xge_hal_fifo_dtr_buffer_finalize - Prepares a descriptor that contains the * single physically contiguous buffer. * * @channelh: Channel handle. * @dtrh: Descriptor handle. * @frag_idx: Index of the data buffer in the Txdl list. * * This API in conjuction with xge_hal_fifo_dtr_buffer_append() prepares * a descriptor that consists of a single physically contiguous buffer * which inturn contains the contents of one or more virtually contiguous * buffers received from the upper layer. * * See Also: xge_hal_fifo_dtr_buffer_append(). */ __HAL_STATIC_FIFO __HAL_INLINE_FIFO void xge_hal_fifo_dtr_buffer_finalize(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh, int frag_idx) { xge_hal_fifo_t *fifo = (xge_hal_fifo_t *)channelh; xge_hal_fifo_txdl_priv_t *txdl_priv; xge_hal_fifo_txd_t *txdp; ptrdiff_t prev_boff; xge_assert(frag_idx < fifo->config->max_frags); txdl_priv = __hal_fifo_txdl_priv(dtrh); txdp = (xge_hal_fifo_txd_t *)dtrh + txdl_priv->frags; if (frag_idx != 0) { txdp->control_1 = txdp->control_2 = 0; } prev_boff = txdl_priv->align_vaddr_start - txdl_priv->align_vaddr; txdp->buffer_pointer = (u64)txdl_priv->align_dma_addr + prev_boff; txdp->control_1 |= XGE_HAL_TXD_BUFFER0_SIZE(txdl_priv->align_dma_offset); txdl_priv->bytes_sent += (unsigned int)txdl_priv->align_dma_offset; fifo->channel.stats.total_buffers++; fifo->channel.stats.copied_buffers++; txdl_priv->frags++; txdl_priv->align_used_frags++; #if defined(XGE_OS_DMA_REQUIRES_SYNC) /* sync pre-mapped buffer */ xge_os_dma_sync(fifo->channel.pdev, txdl_priv->align_dma_handle, txdp->buffer_pointer, 0, txdl_priv->align_dma_offset, XGE_OS_DMA_DIR_TODEVICE); #endif /* increment vaddr_start for the next buffer_append() iteration */ txdl_priv->align_vaddr_start += txdl_priv->align_dma_offset; txdl_priv->align_dma_offset = 0; } /** * xge_hal_fifo_dtr_buffer_set - Set transmit buffer pointer in the * descriptor. * @channelh: Channel handle. * @dtrh: Descriptor handle. * @frag_idx: Index of the data buffer in the caller's scatter-gather listá * (of buffers). * @dma_pointer: DMA address of the data buffer referenced by @frag_idx. * @size: Size of the data buffer (in bytes). * * This API is part of the preparation of the transmit descriptor for posting * (via xge_hal_fifo_dtr_post()). The related "preparation" APIs include * xge_hal_fifo_dtr_mss_set() and xge_hal_fifo_dtr_cksum_set_bits(). * All three APIs fill in the fields of the fifo descriptor, * in accordance with the Xframe specification. * * See also: xge_hal_fifo_dtr_buffer_set_aligned(), * xge_hal_check_alignment(). * * See also: xge_hal_fifo_dtr_reserve(), xge_hal_fifo_dtr_post(), * xge_hal_fifo_dtr_mss_set(), xge_hal_fifo_dtr_cksum_set_bits() * Prepare transmit descriptor for transmission (via * xge_hal_fifo_dtr_post()). * See also: xge_hal_fifo_dtr_vlan_set(). * Note: Compare with xge_hal_fifo_dtr_buffer_set_aligned(). * * Usage: See ex_xmit{}. */ __HAL_STATIC_FIFO __HAL_INLINE_FIFO void xge_hal_fifo_dtr_buffer_set(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh, int frag_idx, dma_addr_t dma_pointer, int size) { xge_hal_fifo_t *fifo = (xge_hal_fifo_t *)channelh; xge_hal_fifo_txdl_priv_t *txdl_priv; xge_hal_fifo_txd_t *txdp; txdl_priv = __hal_fifo_txdl_priv(dtrh); txdp = (xge_hal_fifo_txd_t *)dtrh + txdl_priv->frags; if (frag_idx != 0) { txdp->control_1 = txdp->control_2 = 0; } /* Note: * it is the responsibility of upper layers and not HAL * detect it and skip zero-size fragment */ xge_assert(size > 0); xge_assert(frag_idx < txdl_priv->alloc_frags); txdp->buffer_pointer = (u64)dma_pointer; txdp->control_1 |= XGE_HAL_TXD_BUFFER0_SIZE(size); txdl_priv->bytes_sent += size; fifo->channel.stats.total_buffers++; txdl_priv->frags++; } /** * xge_hal_fifo_dtr_mss_set - Set MSS. * @dtrh: Descriptor handle. * @mss: MSS size for _this_ TCP connection. Passed by TCP stack down to the * ULD, which in turn inserts the MSS into the @dtrh. * * This API is part of the preparation of the transmit descriptor for posting * (via xge_hal_fifo_dtr_post()). The related "preparation" APIs include * xge_hal_fifo_dtr_buffer_set(), xge_hal_fifo_dtr_buffer_set_aligned(), * and xge_hal_fifo_dtr_cksum_set_bits(). * All these APIs fill in the fields of the fifo descriptor, * in accordance with the Xframe specification. * * See also: xge_hal_fifo_dtr_reserve(), * xge_hal_fifo_dtr_post(), xge_hal_fifo_dtr_vlan_set(). * Usage: See ex_xmit{}. */ __HAL_STATIC_FIFO __HAL_INLINE_FIFO void xge_hal_fifo_dtr_mss_set(xge_hal_dtr_h dtrh, int mss) { xge_hal_fifo_txd_t *txdp = (xge_hal_fifo_txd_t *)dtrh; txdp->control_1 |= XGE_HAL_TXD_LSO_COF_CTRL(XGE_HAL_TXD_TCP_LSO); txdp->control_1 |= XGE_HAL_TXD_TCP_LSO_MSS(mss); } /** * xge_hal_fifo_dtr_cksum_set_bits - Offload checksum. * @dtrh: Descriptor handle. * @cksum_bits: Specifies which checksums are to be offloaded: IPv4, * and/or TCP and/or UDP. * * Ask Xframe to calculate IPv4 & transport checksums for _this_ transmit * descriptor. * This API is part of the preparation of the transmit descriptor for posting * (via xge_hal_fifo_dtr_post()). The related "preparation" APIs include * xge_hal_fifo_dtr_mss_set(), xge_hal_fifo_dtr_buffer_set_aligned(), * and xge_hal_fifo_dtr_buffer_set(). * All these APIs fill in the fields of the fifo descriptor, * in accordance with the Xframe specification. * * See also: xge_hal_fifo_dtr_reserve(), * xge_hal_fifo_dtr_post(), XGE_HAL_TXD_TX_CKO_IPV4_EN, * XGE_HAL_TXD_TX_CKO_TCP_EN. * Usage: See ex_xmit{}. */ __HAL_STATIC_FIFO __HAL_INLINE_FIFO void xge_hal_fifo_dtr_cksum_set_bits(xge_hal_dtr_h dtrh, u64 cksum_bits) { xge_hal_fifo_txd_t *txdp = (xge_hal_fifo_txd_t *)dtrh; txdp->control_2 |= cksum_bits; } /** * xge_hal_fifo_dtr_vlan_set - Set VLAN tag. * @dtrh: Descriptor handle. * @vlan_tag: 16bit VLAN tag. * * Insert VLAN tag into specified transmit descriptor. * The actual insertion of the tag into outgoing frame is done by the hardware. * See also: xge_hal_fifo_dtr_buffer_set(), xge_hal_fifo_dtr_mss_set(). */ __HAL_STATIC_FIFO __HAL_INLINE_FIFO void xge_hal_fifo_dtr_vlan_set(xge_hal_dtr_h dtrh, u16 vlan_tag) { xge_hal_fifo_txd_t *txdp = (xge_hal_fifo_txd_t *)dtrh; txdp->control_2 |= XGE_HAL_TXD_VLAN_ENABLE; txdp->control_2 |= XGE_HAL_TXD_VLAN_TAG(vlan_tag); } /** * xge_hal_fifo_is_next_dtr_completed - Checks if the next dtr is completed * @channelh: Channel handle. */ __HAL_STATIC_FIFO __HAL_INLINE_FIFO xge_hal_status_e xge_hal_fifo_is_next_dtr_completed(xge_hal_channel_h channelh) { xge_hal_fifo_txd_t *txdp; xge_hal_dtr_h dtrh; __hal_channel_dtr_try_complete(channelh, &dtrh); txdp = (xge_hal_fifo_txd_t *)dtrh; if (txdp == NULL) { return XGE_HAL_INF_NO_MORE_COMPLETED_DESCRIPTORS; } /* check whether host owns it */ if ( !(txdp->control_1 & XGE_HAL_TXD_LIST_OWN_XENA) ) { xge_assert(txdp->host_control!=0); return XGE_HAL_OK; } /* no more completions */ return XGE_HAL_INF_NO_MORE_COMPLETED_DESCRIPTORS; }