df4d502619
changes: 01 - Enhanced LRO: LRO feature is extended to support multi-buffer mode. Previously, Ethernet frames received in contiguous buffers were offloaded. Now, frames received in multiple non-contiguous buffers can be offloaded, as well. The driver now supports LRO for jumbo frames. 02 - Locks Optimization: The driver code was re-organized to limit the use of locks. Moreover, lock contention was reduced by replacing wait locks with try locks. 03 - Code Optimization: The driver code was re-factored to eliminate some memcpy operations. Fast path loops were optimized. 04 - Tag Creations: Physical Buffer Tags are now optimized based upon frame size. For better performance, Physical Memory Maps are now re-used. 05 - Configuration: Features such as TSO, LRO, and Interrupt Mode can be configured either at load or at run time. Rx buffer mode (mode 1 or mode 2) can be configured at load time through kenv. 06 - Driver Statistics: Run time statistics are enhanced to provide better visibility into the driver performance. 07 - Bug Fixes: The driver contains fixes for the problems discovered and reported since last submission. 08 - MSI support: Added Message Signaled Interrupt feature which currently uses 1 message. 09 Removed feature: Rx 3 buffer mode feature has been removed. Driver now supports 1, 2 and 5 buffer modes of which 2 and 5 buffer modes can be used for header separation. 10 Compiler warning: Fixed compiler warning when compiled for 32 bit system. 11 Copyright notice: Source files are updated with the proper copyright notice. MFC after: 3 days Submitted by: Alicia Pena <Alicia dot Pena at neterion dot com>, Muhammad Shafiq <Muhammad dot Shafiq at neterion dot com>
295 lines
10 KiB
C
295 lines
10 KiB
C
/*-
|
|
* Copyright (c) 2002-2007 Neterion, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
#ifdef XGE_DEBUG_FP
|
|
#include <dev/nxge/include/xgehal-channel.h>
|
|
#endif
|
|
|
|
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL xge_hal_status_e
|
|
__hal_channel_dtr_alloc(xge_hal_channel_h channelh, xge_hal_dtr_h *dtrh)
|
|
{
|
|
void **tmp_arr;
|
|
xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
|
|
#if defined(XGE_HAL_RX_MULTI_FREE_IRQ) || defined(XGE_HAL_TX_MULTI_FREE_IRQ)
|
|
unsigned long flags = 0;
|
|
#endif
|
|
if (channel->terminating) {
|
|
return XGE_HAL_FAIL;
|
|
}
|
|
|
|
if (channel->reserve_length - channel->reserve_top >
|
|
channel->reserve_threshold) {
|
|
|
|
_alloc_after_swap:
|
|
*dtrh = channel->reserve_arr[--channel->reserve_length];
|
|
|
|
xge_debug_channel(XGE_TRACE, "dtrh 0x"XGE_OS_LLXFMT" allocated, "
|
|
"channel %d:%d:%d, reserve_idx %d",
|
|
(unsigned long long)(ulong_t)*dtrh,
|
|
channel->type, channel->post_qid,
|
|
channel->compl_qid, channel->reserve_length);
|
|
|
|
return XGE_HAL_OK;
|
|
}
|
|
|
|
#if defined(XGE_HAL_RX_MULTI_FREE_IRQ) || defined(XGE_HAL_TX_MULTI_FREE_IRQ)
|
|
xge_os_spin_lock_irq(&channel->free_lock, flags);
|
|
#elif defined(XGE_HAL_RX_MULTI_FREE) || defined(XGE_HAL_TX_MULTI_FREE)
|
|
xge_os_spin_lock(&channel->free_lock);
|
|
#endif
|
|
|
|
/* switch between empty and full arrays */
|
|
|
|
/* the idea behind such a design is that by having free and reserved
|
|
* arrays separated we basically separated irq and non-irq parts.
|
|
* i.e. no additional lock need to be done when we free a resource */
|
|
|
|
if (channel->reserve_initial - channel->free_length >
|
|
channel->reserve_threshold) {
|
|
|
|
tmp_arr = channel->reserve_arr;
|
|
channel->reserve_arr = channel->free_arr;
|
|
channel->reserve_length = channel->reserve_initial;
|
|
channel->free_arr = tmp_arr;
|
|
channel->reserve_top = channel->free_length;
|
|
channel->free_length = channel->reserve_initial;
|
|
|
|
channel->stats.reserve_free_swaps_cnt++;
|
|
|
|
xge_debug_channel(XGE_TRACE,
|
|
"switch on channel %d:%d:%d, reserve_length %d, "
|
|
"free_length %d", channel->type, channel->post_qid,
|
|
channel->compl_qid, channel->reserve_length,
|
|
channel->free_length);
|
|
|
|
#if defined(XGE_HAL_RX_MULTI_FREE_IRQ) || defined(XGE_HAL_TX_MULTI_FREE_IRQ)
|
|
xge_os_spin_unlock_irq(&channel->free_lock, flags);
|
|
#elif defined(XGE_HAL_RX_MULTI_FREE) || defined(XGE_HAL_TX_MULTI_FREE)
|
|
xge_os_spin_unlock(&channel->free_lock);
|
|
#endif
|
|
|
|
goto _alloc_after_swap;
|
|
}
|
|
|
|
#if defined(XGE_HAL_RX_MULTI_FREE_IRQ) || defined(XGE_HAL_TX_MULTI_FREE_IRQ)
|
|
xge_os_spin_unlock_irq(&channel->free_lock, flags);
|
|
#elif defined(XGE_HAL_RX_MULTI_FREE) || defined(XGE_HAL_TX_MULTI_FREE)
|
|
xge_os_spin_unlock(&channel->free_lock);
|
|
#endif
|
|
|
|
xge_debug_channel(XGE_TRACE, "channel %d:%d:%d is empty!",
|
|
channel->type, channel->post_qid,
|
|
channel->compl_qid);
|
|
|
|
channel->stats.full_cnt++;
|
|
|
|
*dtrh = NULL;
|
|
return XGE_HAL_INF_OUT_OF_DESCRIPTORS;
|
|
}
|
|
|
|
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
|
|
__hal_channel_dtr_restore(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh,
|
|
int offset)
|
|
{
|
|
xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
|
|
|
|
/* restore a previously allocated dtrh at current offset and update
|
|
* the available reserve length accordingly. If dtrh is null just
|
|
* update the reserve length, only */
|
|
|
|
if (dtrh) {
|
|
channel->reserve_arr[channel->reserve_length + offset] = dtrh;
|
|
xge_debug_channel(XGE_TRACE, "dtrh 0x"XGE_OS_LLXFMT" restored for "
|
|
"channel %d:%d:%d, offset %d at reserve index %d, ",
|
|
(unsigned long long)(ulong_t)dtrh, channel->type,
|
|
channel->post_qid, channel->compl_qid, offset,
|
|
channel->reserve_length + offset);
|
|
}
|
|
else {
|
|
channel->reserve_length += offset;
|
|
xge_debug_channel(XGE_TRACE, "channel %d:%d:%d, restored "
|
|
"for offset %d, new reserve_length %d, free length %d",
|
|
channel->type, channel->post_qid, channel->compl_qid,
|
|
offset, channel->reserve_length, channel->free_length);
|
|
}
|
|
}
|
|
|
|
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
|
|
__hal_channel_dtr_post(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh)
|
|
{
|
|
xge_hal_channel_t *channel = (xge_hal_channel_t*)channelh;
|
|
|
|
xge_assert(channel->work_arr[channel->post_index] == NULL);
|
|
|
|
channel->work_arr[channel->post_index++] = dtrh;
|
|
|
|
/* wrap-around */
|
|
if (channel->post_index == channel->length)
|
|
channel->post_index = 0;
|
|
}
|
|
|
|
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
|
|
__hal_channel_dtr_try_complete(xge_hal_channel_h channelh, xge_hal_dtr_h *dtrh)
|
|
{
|
|
xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
|
|
|
|
xge_assert(channel->work_arr);
|
|
xge_assert(channel->compl_index < channel->length);
|
|
|
|
*dtrh = channel->work_arr[channel->compl_index];
|
|
}
|
|
|
|
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
|
|
__hal_channel_dtr_complete(xge_hal_channel_h channelh)
|
|
{
|
|
xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
|
|
|
|
channel->work_arr[channel->compl_index] = NULL;
|
|
|
|
/* wrap-around */
|
|
if (++channel->compl_index == channel->length)
|
|
channel->compl_index = 0;
|
|
|
|
channel->stats.total_compl_cnt++;
|
|
}
|
|
|
|
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
|
|
__hal_channel_dtr_free(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh)
|
|
{
|
|
xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
|
|
|
|
channel->free_arr[--channel->free_length] = dtrh;
|
|
|
|
xge_debug_channel(XGE_TRACE, "dtrh 0x"XGE_OS_LLXFMT" freed, "
|
|
"channel %d:%d:%d, new free_length %d",
|
|
(unsigned long long)(ulong_t)dtrh,
|
|
channel->type, channel->post_qid,
|
|
channel->compl_qid, channel->free_length);
|
|
}
|
|
|
|
/**
|
|
* xge_hal_channel_dtr_count
|
|
* @channelh: Channel handle. Obtained via xge_hal_channel_open().
|
|
*
|
|
* Retreive number of DTRs available. This function can not be called
|
|
* from data path.
|
|
*/
|
|
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL int
|
|
xge_hal_channel_dtr_count(xge_hal_channel_h channelh)
|
|
{
|
|
xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
|
|
|
|
return ((channel->reserve_length - channel->reserve_top) +
|
|
(channel->reserve_initial - channel->free_length) -
|
|
channel->reserve_threshold);
|
|
}
|
|
|
|
/**
|
|
* xge_hal_channel_userdata - Get user-specified channel context.
|
|
* @channelh: Channel handle. Obtained via xge_hal_channel_open().
|
|
*
|
|
* Returns: per-channel "user data", which can be any ULD-defined context.
|
|
* The %userdata "gets" into the channel at open time
|
|
* (see xge_hal_channel_open()).
|
|
*
|
|
* See also: xge_hal_channel_open().
|
|
*/
|
|
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void*
|
|
xge_hal_channel_userdata(xge_hal_channel_h channelh)
|
|
{
|
|
xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
|
|
|
|
return channel->userdata;
|
|
}
|
|
|
|
/**
|
|
* xge_hal_channel_id - Get channel ID.
|
|
* @channelh: Channel handle. Obtained via xge_hal_channel_open().
|
|
*
|
|
* Returns: channel ID. For link layer channel id is the number
|
|
* in the range from 0 to 7 that identifies hardware ring or fifo,
|
|
* depending on the channel type.
|
|
*/
|
|
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL int
|
|
xge_hal_channel_id(xge_hal_channel_h channelh)
|
|
{
|
|
xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
|
|
|
|
return channel->post_qid;
|
|
}
|
|
|
|
/**
|
|
* xge_hal_check_alignment - Check buffer alignment and calculate the
|
|
* "misaligned" portion.
|
|
* @dma_pointer: DMA address of the buffer.
|
|
* @size: Buffer size, in bytes.
|
|
* @alignment: Alignment "granularity" (see below), in bytes.
|
|
* @copy_size: Maximum number of bytes to "extract" from the buffer
|
|
* (in order to spost it as a separate scatter-gather entry). See below.
|
|
*
|
|
* Check buffer alignment and calculate "misaligned" portion, if exists.
|
|
* The buffer is considered aligned if its address is multiple of
|
|
* the specified @alignment. If this is the case,
|
|
* xge_hal_check_alignment() returns zero.
|
|
* Otherwise, xge_hal_check_alignment() uses the last argument,
|
|
* @copy_size,
|
|
* to calculate the size to "extract" from the buffer. The @copy_size
|
|
* may or may not be equal @alignment. The difference between these two
|
|
* arguments is that the @alignment is used to make the decision: aligned
|
|
* or not aligned. While the @copy_size is used to calculate the portion
|
|
* of the buffer to "extract", i.e. to post as a separate entry in the
|
|
* transmit descriptor. For example, the combination
|
|
* @alignment=8 and @copy_size=64 will work okay on AMD Opteron boxes.
|
|
*
|
|
* Note: @copy_size should be a multiple of @alignment. In many practical
|
|
* cases @copy_size and @alignment will probably be equal.
|
|
*
|
|
* See also: xge_hal_fifo_dtr_buffer_set_aligned().
|
|
*/
|
|
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL int
|
|
xge_hal_check_alignment(dma_addr_t dma_pointer, int size, int alignment,
|
|
int copy_size)
|
|
{
|
|
int misaligned_size;
|
|
|
|
misaligned_size = (int)(dma_pointer & (alignment - 1));
|
|
if (!misaligned_size) {
|
|
return 0;
|
|
}
|
|
|
|
if (size > copy_size) {
|
|
misaligned_size = (int)(dma_pointer & (copy_size - 1));
|
|
misaligned_size = copy_size - misaligned_size;
|
|
} else {
|
|
misaligned_size = size;
|
|
}
|
|
|
|
return misaligned_size;
|
|
}
|
|
|