nxge_hio.c revision 4ba491f53199ecfd5a928fdce7dc8f8aa9de4b9e
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* nxge_hio.c
*
* This file manages the virtualization resources for Neptune
* devices. That is, it implements a hybrid I/O (HIO) approach in the
* Solaris kernel, whereby a guest domain on an LDOMs server may
* request & use hardware resources from the service domain.
*
*/
#include <sys/mac_provider.h>
#include <sys/nxge/nxge_impl.h>
#include <sys/nxge/nxge_fzc.h>
#include <sys/nxge/nxge_rxdma.h>
#include <sys/nxge/nxge_txdma.h>
#include <sys/nxge/nxge_hio.h>
#define NXGE_HIO_SHARE_MIN_CHANNELS 2
#define NXGE_HIO_SHARE_MAX_CHANNELS 2
/*
* External prototypes
*/
extern npi_status_t npi_rxdma_dump_rdc_table(npi_handle_t, uint8_t);
/* The following function may be found in nxge_main.c */
extern int nxge_m_mmac_remove(void *arg, int slot);
extern int nxge_m_mmac_add_g(void *arg, const uint8_t *maddr, int rdctbl,
boolean_t usetbl);
/* The following function may be found in nxge_[t|r]xdma.c */
extern npi_status_t nxge_txdma_channel_disable(nxge_t *, int);
extern nxge_status_t nxge_disable_rxdma_channel(nxge_t *, uint16_t);
/*
* Local prototypes
*/
static void nxge_grp_dc_append(nxge_t *, nxge_grp_t *, nxge_hio_dc_t *);
static nxge_hio_dc_t *nxge_grp_dc_unlink(nxge_t *, nxge_grp_t *, int);
static void nxge_grp_dc_map(nxge_grp_t *group);
/*
* These functions are used by both service & guest domains to
* decide whether they're running in an LDOMs/XEN environment
* or not. If so, then the Hybrid I/O (HIO) module is initialized.
*/
/*
* nxge_get_environs
*
* Figure out if we are in a guest domain or not.
*
* Arguments:
* nxge
*
* Notes:
*
* Context:
* Any domain
*/
void
nxge_get_environs(
nxge_t *nxge)
{
char *string;
/*
* In the beginning, assume that we are running sans LDOMs/XEN.
*/
nxge->environs = SOLARIS_DOMAIN;
/*
* Are we a hybrid I/O (HIO) guest domain driver?
*/
if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, nxge->dip,
DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
"niutype", &string)) == DDI_PROP_SUCCESS) {
if (strcmp(string, "n2niu") == 0) {
nxge->environs = SOLARIS_GUEST_DOMAIN;
/* So we can allocate properly-aligned memory. */
nxge->niu_type = N2_NIU;
NXGE_DEBUG_MSG((nxge, HIO_CTL,
"Hybrid IO-capable guest domain"));
}
ddi_prop_free(string);
}
}
#if !defined(sun4v)
/*
* nxge_hio_init
*
* Initialize the HIO module of the NXGE driver.
*
* Arguments:
* nxge
*
* Notes:
* This is the non-hybrid I/O version of this function.
*
* Context:
* Any domain
*/
int
nxge_hio_init(nxge_t *nxge)
{
nxge_hio_data_t *nhd;
int i;
nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
if (nhd == 0) {
nhd = KMEM_ZALLOC(sizeof (*nhd), KM_SLEEP);
MUTEX_INIT(&nhd->lock, NULL, MUTEX_DRIVER, NULL);
nxge->nxge_hw_p->hio = (uintptr_t)nhd;
}
/*
* Initialize share and ring group structures.
*/
for (i = 0; i < NXGE_MAX_TDCS; i++)
nxge->tdc_is_shared[i] = B_FALSE;
for (i = 0; i < NXGE_MAX_TDC_GROUPS; i++) {
nxge->tx_hio_groups[i].ghandle = NULL;
nxge->tx_hio_groups[i].nxgep = nxge;
nxge->tx_hio_groups[i].type = MAC_RING_TYPE_TX;
nxge->tx_hio_groups[i].gindex = 0;
nxge->tx_hio_groups[i].sindex = 0;
}
for (i = 0; i < NXGE_MAX_RDC_GROUPS; i++) {
nxge->rx_hio_groups[i].ghandle = NULL;
nxge->rx_hio_groups[i].nxgep = nxge;
nxge->rx_hio_groups[i].type = MAC_RING_TYPE_RX;
nxge->rx_hio_groups[i].gindex = 0;
nxge->rx_hio_groups[i].sindex = 0;
nxge->rx_hio_groups[i].started = B_FALSE;
nxge->rx_hio_groups[i].port_default_grp = B_FALSE;
nxge->rx_hio_groups[i].rdctbl = -1;
nxge->rx_hio_groups[i].n_mac_addrs = 0;
}
nhd->hio.ldoms = B_FALSE;
return (NXGE_OK);
}
#endif
void
nxge_hio_uninit(nxge_t *nxge)
{
nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
ASSERT(nxge->nxge_hw_p->ndevs == 0);
if (nhd != NULL) {
MUTEX_DESTROY(&nhd->lock);
KMEM_FREE(nhd, sizeof (*nhd));
nxge->nxge_hw_p->hio = 0;
}
}
/*
* nxge_dci_map
*
* Map a DMA channel index to a channel number.
*
* Arguments:
* instance The instance number of the driver.
* type The type of channel this is: Tx or Rx.
* index The index to convert to a channel number
*
* Notes:
* This function is called by nxge_ndd.c:nxge_param_set_port_rdc()
*
* Context:
* Any domain
*/
int
nxge_dci_map(
nxge_t *nxge,
vpc_type_t type,
int index)
{
nxge_grp_set_t *set;
int dc;
switch (type) {
case VP_BOUND_TX:
set = &nxge->tx_set;
break;
case VP_BOUND_RX:
set = &nxge->rx_set;
break;
}
for (dc = 0; dc < NXGE_MAX_TDCS; dc++) {
if ((1 << dc) & set->owned.map) {
if (index == 0)
return (dc);
else
index--;
}
}
return (-1);
}
/*
* ---------------------------------------------------------------------
* These are the general-purpose DMA channel group functions. That is,
* these functions are used to manage groups of TDCs or RDCs in an HIO
* environment.
*
* But is also expected that in the future they will be able to manage
* Crossbow groups.
* ---------------------------------------------------------------------
*/
/*
* nxge_grp_cleanup(p_nxge_t nxge)
*
* Remove all outstanding groups.
*
* Arguments:
* nxge
*/
void
nxge_grp_cleanup(p_nxge_t nxge)
{
nxge_grp_set_t *set;
int i;
MUTEX_ENTER(&nxge->group_lock);
/*
* Find RX groups that need to be cleaned up.
*/
set = &nxge->rx_set;
for (i = 0; i < NXGE_LOGICAL_GROUP_MAX; i++) {
if (set->group[i] != NULL) {
KMEM_FREE(set->group[i], sizeof (nxge_grp_t));
set->group[i] = NULL;
}
}
/*
* Find TX groups that need to be cleaned up.
*/
set = &nxge->tx_set;
for (i = 0; i < NXGE_LOGICAL_GROUP_MAX; i++) {
if (set->group[i] != NULL) {
KMEM_FREE(set->group[i], sizeof (nxge_grp_t));
set->group[i] = NULL;
}
}
MUTEX_EXIT(&nxge->group_lock);
}
/*
* nxge_grp_add
*
* Add a group to an instance of NXGE.
*
* Arguments:
* nxge
* type Tx or Rx
*
* Notes:
*
* Context:
* Any domain
*/
nxge_grp_t *
nxge_grp_add(
nxge_t *nxge,
nxge_grp_type_t type)
{
nxge_grp_set_t *set;
nxge_grp_t *group;
int i;
group = KMEM_ZALLOC(sizeof (*group), KM_SLEEP);
group->nxge = nxge;
MUTEX_ENTER(&nxge->group_lock);
switch (type) {
case NXGE_TRANSMIT_GROUP:
case EXT_TRANSMIT_GROUP:
set = &nxge->tx_set;
break;
default:
set = &nxge->rx_set;
break;
}
group->type = type;
group->active = B_TRUE;
group->sequence = set->sequence++;
/* Find an empty slot for this logical group. */
for (i = 0; i < NXGE_LOGICAL_GROUP_MAX; i++) {
if (set->group[i] == 0) {
group->index = i;
set->group[i] = group;
NXGE_DC_SET(set->lg.map, i);
set->lg.count++;
break;
}
}
MUTEX_EXIT(&nxge->group_lock);
NXGE_DEBUG_MSG((nxge, HIO_CTL,
"nxge_grp_add: %cgroup = %d.%d",
type == NXGE_TRANSMIT_GROUP ? 't' : 'r',
nxge->mac.portnum, group->sequence));
return (group);
}
void
nxge_grp_remove(
nxge_t *nxge,
nxge_grp_t *group) /* The group to remove. */
{
nxge_grp_set_t *set;
vpc_type_t type;
MUTEX_ENTER(&nxge->group_lock);
switch (group->type) {
case NXGE_TRANSMIT_GROUP:
case EXT_TRANSMIT_GROUP:
set = &nxge->tx_set;
break;
default:
set = &nxge->rx_set;
break;
}
if (set->group[group->index] != group) {
MUTEX_EXIT(&nxge->group_lock);
return;
}
set->group[group->index] = 0;
NXGE_DC_RESET(set->lg.map, group->index);
set->lg.count--;
/* While inside the mutex, deactivate <group>. */
group->active = B_FALSE;
MUTEX_EXIT(&nxge->group_lock);
NXGE_DEBUG_MSG((nxge, HIO_CTL,
"nxge_grp_remove(%c.%d.%d) called",
group->type == NXGE_TRANSMIT_GROUP ? 't' : 'r',
nxge->mac.portnum, group->sequence));
/* Now, remove any DCs which are still active. */
switch (group->type) {
default:
type = VP_BOUND_TX;
break;
case NXGE_RECEIVE_GROUP:
case EXT_RECEIVE_GROUP:
type = VP_BOUND_RX;
}
while (group->dc) {
nxge_grp_dc_remove(nxge, type, group->dc->channel);
}
KMEM_FREE(group, sizeof (*group));
}
/*
* nxge_grp_dc_add
*
* Add a DMA channel to a VR/Group.
*
* Arguments:
* nxge
* channel The channel to add.
* Notes:
*
* Context:
* Any domain
*/
/* ARGSUSED */
int
nxge_grp_dc_add(
nxge_t *nxge,
nxge_grp_t *group, /* The group to add <channel> to. */
vpc_type_t type, /* Rx or Tx */
int channel) /* A physical/logical channel number */
{
nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
nxge_hio_dc_t *dc;
nxge_grp_set_t *set;
nxge_status_t status = NXGE_OK;
NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_grp_dc_add"));
if (group == 0)
return (0);
switch (type) {
case VP_BOUND_TX:
set = &nxge->tx_set;
if (channel > NXGE_MAX_TDCS) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
"nxge_grp_dc_add: TDC = %d", channel));
return (NXGE_ERROR);
}
break;
case VP_BOUND_RX:
set = &nxge->rx_set;
if (channel > NXGE_MAX_RDCS) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
"nxge_grp_dc_add: RDC = %d", channel));
return (NXGE_ERROR);
}
break;
default:
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
"nxge_grp_dc_add: unknown type channel(%d)", channel));
}
NXGE_DEBUG_MSG((nxge, HIO_CTL,
"nxge_grp_dc_add: %cgroup = %d.%d.%d, channel = %d",
type == VP_BOUND_TX ? 't' : 'r',
nxge->mac.portnum, group->sequence, group->count, channel));
MUTEX_ENTER(&nxge->group_lock);
if (group->active != B_TRUE) {
/* We may be in the process of removing this group. */
MUTEX_EXIT(&nxge->group_lock);
return (NXGE_ERROR);
}
MUTEX_EXIT(&nxge->group_lock);
if (!(dc = nxge_grp_dc_find(nxge, type, channel))) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
"nxge_grp_dc_add(%d): DC FIND failed", channel));
return (NXGE_ERROR);
}
MUTEX_ENTER(&nhd->lock);
if (dc->group) {
MUTEX_EXIT(&nhd->lock);
/* This channel is already in use! */
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
"nxge_grp_dc_add(%d): channel already in group", channel));
return (NXGE_ERROR);
}
dc->next = 0;
dc->page = channel;
dc->channel = (nxge_channel_t)channel;
dc->type = type;
if (type == VP_BOUND_RX) {
dc->init = nxge_init_rxdma_channel;
dc->uninit = nxge_uninit_rxdma_channel;
} else {
dc->init = nxge_init_txdma_channel;
dc->uninit = nxge_uninit_txdma_channel;
}
dc->group = group;
if (isLDOMguest(nxge))
(void) nxge_hio_ldsv_add(nxge, dc);
NXGE_DC_SET(set->owned.map, channel);
set->owned.count++;
MUTEX_EXIT(&nhd->lock);
if ((status = (*dc->init)(nxge, channel)) != NXGE_OK) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
"nxge_grp_dc_add(%d): channel init failed", channel));
MUTEX_ENTER(&nhd->lock);
(void) memset(dc, 0, sizeof (*dc));
NXGE_DC_RESET(set->owned.map, channel);
set->owned.count--;
MUTEX_EXIT(&nhd->lock);
return (NXGE_ERROR);
}
nxge_grp_dc_append(nxge, group, dc);
if (type == VP_BOUND_TX) {
MUTEX_ENTER(&nhd->lock);
nxge->tdc_is_shared[channel] = B_FALSE;
MUTEX_EXIT(&nhd->lock);
}
NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_grp_dc_add"));
return ((int)status);
}
void
nxge_grp_dc_remove(
nxge_t *nxge,
vpc_type_t type,
int channel)
{
nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
nxge_hio_dc_t *dc;
nxge_grp_set_t *set;
nxge_grp_t *group;
dc_uninit_t uninit;
NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_grp_dc_remove"));
if ((dc = nxge_grp_dc_find(nxge, type, channel)) == 0)
goto nxge_grp_dc_remove_exit;
if ((dc->group == NULL) && (dc->next == 0) &&
(dc->channel == 0) && (dc->page == 0) && (dc->type == 0)) {
goto nxge_grp_dc_remove_exit;
}
group = (nxge_grp_t *)dc->group;
if (isLDOMguest(nxge)) {
(void) nxge_hio_intr_remove(nxge, type, channel);
}
NXGE_DEBUG_MSG((nxge, HIO_CTL,
"DC remove: group = %d.%d.%d, %cdc %d",
nxge->mac.portnum, group->sequence, group->count,
type == VP_BOUND_TX ? 't' : 'r', dc->channel));
MUTEX_ENTER(&nhd->lock);
set = dc->type == VP_BOUND_TX ? &nxge->tx_set : &nxge->rx_set;
/* Remove the DC from its group. */
if (nxge_grp_dc_unlink(nxge, group, channel) != dc) {
MUTEX_EXIT(&nhd->lock);
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
"nxge_grp_dc_remove(%d) failed", channel));
goto nxge_grp_dc_remove_exit;
}
uninit = dc->uninit;
channel = dc->channel;
NXGE_DC_RESET(set->owned.map, channel);
set->owned.count--;
(void) memset(dc, 0, sizeof (*dc));
MUTEX_EXIT(&nhd->lock);
(*uninit)(nxge, channel);
nxge_grp_dc_remove_exit:
NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_grp_dc_remove"));
}
nxge_hio_dc_t *
nxge_grp_dc_find(
nxge_t *nxge,
vpc_type_t type, /* Rx or Tx */
int channel)
{
nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
nxge_hio_dc_t *current;
current = (type == VP_BOUND_TX) ? &nhd->tdc[0] : &nhd->rdc[0];
if (!isLDOMguest(nxge)) {
return (&current[channel]);
} else {
/* We're in a guest domain. */
int i, limit = (type == VP_BOUND_TX) ?
NXGE_MAX_TDCS : NXGE_MAX_RDCS;
MUTEX_ENTER(&nhd->lock);
for (i = 0; i < limit; i++, current++) {
if (current->channel == channel) {
if (current->vr && current->vr->nxge ==
(uintptr_t)nxge) {
MUTEX_EXIT(&nhd->lock);
return (current);
}
}
}
MUTEX_EXIT(&nhd->lock);
}
return (0);
}
/*
* nxge_grp_dc_append
*
* Append a DMA channel to a group.
*
* Arguments:
* nxge
* group The group to append to
* dc The DMA channel to append
*
* Notes:
*
* Context:
* Any domain
*/
static
void
nxge_grp_dc_append(
nxge_t *nxge,
nxge_grp_t *group,
nxge_hio_dc_t *dc)
{
MUTEX_ENTER(&nxge->group_lock);
if (group->dc == 0) {
group->dc = dc;
} else {
nxge_hio_dc_t *current = group->dc;
do {
if (current->next == 0) {
current->next = dc;
break;
}
current = current->next;
} while (current);
}
NXGE_DC_SET(group->map, dc->channel);
nxge_grp_dc_map(group);
group->count++;
MUTEX_EXIT(&nxge->group_lock);
}
/*
* nxge_grp_dc_unlink
*
* Unlink a DMA channel fromits linked list (group).
*
* Arguments:
* nxge
* group The group (linked list) to unlink from
* dc The DMA channel to append
*
* Notes:
*
* Context:
* Any domain
*/
nxge_hio_dc_t *
nxge_grp_dc_unlink(
nxge_t *nxge,
nxge_grp_t *group,
int channel)
{
nxge_hio_dc_t *current, *previous;
MUTEX_ENTER(&nxge->group_lock);
if (group == NULL) {
MUTEX_EXIT(&nxge->group_lock);
return (0);
}
if ((current = group->dc) == 0) {
MUTEX_EXIT(&nxge->group_lock);
return (0);
}
previous = 0;
do {
if (current->channel == channel) {
if (previous)
previous->next = current->next;
else
group->dc = current->next;
break;
}
previous = current;
current = current->next;
} while (current);
if (current == 0) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
"DC unlink: DC %d not found", channel));
} else {
current->next = 0;
current->group = 0;
NXGE_DC_RESET(group->map, channel);
group->count--;
}
nxge_grp_dc_map(group);
MUTEX_EXIT(&nxge->group_lock);
return (current);
}
/*
* nxge_grp_dc_map
*
* Map a linked list to an array of channel numbers.
*
* Arguments:
* nxge
* group The group to remap.
*
* Notes:
* It is expected that the caller will hold the correct mutex.
*
* Context:
* Service domain
*/
void
nxge_grp_dc_map(
nxge_grp_t *group)
{
nxge_channel_t *legend;
nxge_hio_dc_t *dc;
(void) memset(group->legend, 0, sizeof (group->legend));
legend = group->legend;
dc = group->dc;
while (dc) {
*legend = dc->channel;
legend++;
dc = dc->next;
}
}
/*
* ---------------------------------------------------------------------
* These are HIO debugging functions.
* ---------------------------------------------------------------------
*/
/*
* nxge_delay
*
* Delay <seconds> number of seconds.
*
* Arguments:
* nxge
* group The group to append to
* dc The DMA channel to append
*
* Notes:
* This is a developer-only function.
*
* Context:
* Any domain
*/
void
nxge_delay(
int seconds)
{
delay(drv_usectohz(seconds * 1000000));
}
static dmc_reg_name_t rx_names[] = {
{ "RXDMA_CFIG1", 0 },
{ "RXDMA_CFIG2", 8 },
{ "RBR_CFIG_A", 0x10 },
{ "RBR_CFIG_B", 0x18 },
{ "RBR_KICK", 0x20 },
{ "RBR_STAT", 0x28 },
{ "RBR_HDH", 0x30 },
{ "RBR_HDL", 0x38 },
{ "RCRCFIG_A", 0x40 },
{ "RCRCFIG_B", 0x48 },
{ "RCRSTAT_A", 0x50 },
{ "RCRSTAT_B", 0x58 },
{ "RCRSTAT_C", 0x60 },
{ "RX_DMA_ENT_MSK", 0x68 },
{ "RX_DMA_CTL_STAT", 0x70 },
{ "RCR_FLSH", 0x78 },
{ "RXMISC", 0x90 },
{ "RX_DMA_CTL_STAT_DBG", 0x98 },
{ 0, -1 }
};
static dmc_reg_name_t tx_names[] = {
{ "Tx_RNG_CFIG", 0 },
{ "Tx_RNG_HDL", 0x10 },
{ "Tx_RNG_KICK", 0x18 },
{ "Tx_ENT_MASK", 0x20 },
{ "Tx_CS", 0x28 },
{ "TxDMA_MBH", 0x30 },
{ "TxDMA_MBL", 0x38 },
{ "TxDMA_PRE_ST", 0x40 },
{ "Tx_RNG_ERR_LOGH", 0x48 },
{ "Tx_RNG_ERR_LOGL", 0x50 },
{ "TDMC_INTR_DBG", 0x60 },
{ "Tx_CS_DBG", 0x68 },
{ 0, -1 }
};
/*
* nxge_xx2str
*
* Translate a register address into a string.
*
* Arguments:
* offset The address of the register to translate.
*
* Notes:
* These are developer-only function.
*
* Context:
* Any domain
*/
const char *
nxge_rx2str(
int offset)
{
dmc_reg_name_t *reg = &rx_names[0];
offset &= DMA_CSR_MASK;
while (reg->name) {
if (offset == reg->offset)
return (reg->name);
reg++;
}
return (0);
}
const char *
nxge_tx2str(
int offset)
{
dmc_reg_name_t *reg = &tx_names[0];
offset &= DMA_CSR_MASK;
while (reg->name) {
if (offset == reg->offset)
return (reg->name);
reg++;
}
return (0);
}
/*
* nxge_ddi_perror
*
* Map a DDI error number to a string.
*
* Arguments:
* ddi_error The DDI error number to map.
*
* Notes:
*
* Context:
* Any domain
*/
const char *
nxge_ddi_perror(
int ddi_error)
{
switch (ddi_error) {
case DDI_SUCCESS:
return ("DDI_SUCCESS");
case DDI_FAILURE:
return ("DDI_FAILURE");
case DDI_NOT_WELL_FORMED:
return ("DDI_NOT_WELL_FORMED");
case DDI_EAGAIN:
return ("DDI_EAGAIN");
case DDI_EINVAL:
return ("DDI_EINVAL");
case DDI_ENOTSUP:
return ("DDI_ENOTSUP");
case DDI_EPENDING:
return ("DDI_EPENDING");
case DDI_ENOMEM:
return ("DDI_ENOMEM");
case DDI_EBUSY:
return ("DDI_EBUSY");
case DDI_ETRANSPORT:
return ("DDI_ETRANSPORT");
case DDI_ECONTEXT:
return ("DDI_ECONTEXT");
default:
return ("Unknown error");
}
}
/*
* ---------------------------------------------------------------------
* These are Sun4v HIO function definitions
* ---------------------------------------------------------------------
*/
#if defined(sun4v)
/*
* Local prototypes
*/
static nxge_hio_vr_t *nxge_hio_vr_share(nxge_t *);
static void nxge_hio_unshare(nxge_hio_vr_t *);
static int nxge_hio_addres(nxge_hio_vr_t *, mac_ring_type_t, uint64_t *);
static void nxge_hio_remres(nxge_hio_vr_t *, mac_ring_type_t, res_map_t);
static void nxge_hio_tdc_unshare(nxge_t *nxge, int dev_grpid, int channel);
static void nxge_hio_rdc_unshare(nxge_t *nxge, int dev_grpid, int channel);
static int nxge_hio_dc_share(nxge_t *, nxge_hio_vr_t *, mac_ring_type_t, int);
static void nxge_hio_dc_unshare(nxge_t *, nxge_hio_vr_t *,
mac_ring_type_t, int);
/*
* nxge_hio_init
*
* Initialize the HIO module of the NXGE driver.
*
* Arguments:
* nxge
*
* Notes:
*
* Context:
* Any domain
*/
int
nxge_hio_init(
nxge_t *nxge)
{
nxge_hio_data_t *nhd;
int i, region;
nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
if (nhd == 0) {
nhd = KMEM_ZALLOC(sizeof (*nhd), KM_SLEEP);
MUTEX_INIT(&nhd->lock, NULL, MUTEX_DRIVER, NULL);
nxge->nxge_hw_p->hio = (uintptr_t)nhd;
}
if ((nxge->environs == SOLARIS_DOMAIN) &&
(nxge->niu_type == N2_NIU)) {
if (nxge->niu_hsvc_available == B_TRUE) {
hsvc_info_t *niu_hsvc = &nxge->niu_hsvc;
if (niu_hsvc->hsvc_major == 1 &&
niu_hsvc->hsvc_minor == 1)
nxge->environs = SOLARIS_SERVICE_DOMAIN;
NXGE_DEBUG_MSG((nxge, HIO_CTL,
"nxge_hio_init: hypervisor services "
"version %d.%d",
niu_hsvc->hsvc_major, niu_hsvc->hsvc_minor));
}
}
/*
* Initialize share and ring group structures.
*/
for (i = 0; i < NXGE_MAX_TDC_GROUPS; i++) {
nxge->tx_hio_groups[i].ghandle = NULL;
nxge->tx_hio_groups[i].nxgep = nxge;
nxge->tx_hio_groups[i].type = MAC_RING_TYPE_TX;
nxge->tx_hio_groups[i].gindex = 0;
nxge->tx_hio_groups[i].sindex = 0;
}
for (i = 0; i < NXGE_MAX_RDC_GROUPS; i++) {
nxge->rx_hio_groups[i].ghandle = NULL;
nxge->rx_hio_groups[i].nxgep = nxge;
nxge->rx_hio_groups[i].type = MAC_RING_TYPE_RX;
nxge->rx_hio_groups[i].gindex = 0;
nxge->rx_hio_groups[i].sindex = 0;
nxge->rx_hio_groups[i].started = B_FALSE;
nxge->rx_hio_groups[i].port_default_grp = B_FALSE;
nxge->rx_hio_groups[i].rdctbl = -1;
nxge->rx_hio_groups[i].n_mac_addrs = 0;
}
if (!isLDOMs(nxge)) {
nhd->hio.ldoms = B_FALSE;
return (NXGE_OK);
}
nhd->hio.ldoms = B_TRUE;
/*
* Fill in what we can.
*/
for (region = 0; region < NXGE_VR_SR_MAX; region++) {
nhd->vr[region].region = region;
}
nhd->vrs = NXGE_VR_SR_MAX - 2;
/*
* Initialize the share stuctures.
*/
for (i = 0; i < NXGE_MAX_TDCS; i++)
nxge->tdc_is_shared[i] = B_FALSE;
for (i = 0; i < NXGE_VR_SR_MAX; i++) {
nxge->shares[i].nxgep = nxge;
nxge->shares[i].index = 0;
nxge->shares[i].vrp = NULL;
nxge->shares[i].tmap = 0;
nxge->shares[i].rmap = 0;
nxge->shares[i].rxgroup = 0;
nxge->shares[i].active = B_FALSE;
}
/* Fill in the HV HIO function pointers. */
nxge_hio_hv_init(nxge);
if (isLDOMservice(nxge)) {
NXGE_DEBUG_MSG((nxge, HIO_CTL,
"Hybrid IO-capable service domain"));
return (NXGE_OK);
} else {
/*
* isLDOMguest(nxge) == B_TRUE
*/
nx_vio_fp_t *vio;
nhd->type = NXGE_HIO_TYPE_GUEST;
vio = &nhd->hio.vio;
vio->__register = (vio_net_resource_reg_t)
modgetsymvalue("vio_net_resource_reg", 0);
vio->unregister = (vio_net_resource_unreg_t)
modgetsymvalue("vio_net_resource_unreg", 0);
if (vio->__register == 0 || vio->unregister == 0) {
NXGE_ERROR_MSG((nxge, VIR_CTL, "vio_net is absent!"));
return (NXGE_ERROR);
}
}
return (0);
}
#endif /* defined(sun4v) */
static int
nxge_hio_group_mac_add(nxge_t *nxge, nxge_ring_group_t *g,
const uint8_t *macaddr)
{
int rv;
nxge_rdc_grp_t *group;
mutex_enter(nxge->genlock);
/*
* Initialize the NXGE RDC table data structure.
*/
group = &nxge->pt_config.rdc_grps[g->rdctbl];
if (!group->flag) {
group->port = NXGE_GET_PORT_NUM(nxge->function_num);
group->config_method = RDC_TABLE_ENTRY_METHOD_REP;
group->flag = B_TRUE; /* This group has been configured. */
}
mutex_exit(nxge->genlock);
/*
* Add the MAC address.
*/
if ((rv = nxge_m_mmac_add_g((void *)nxge, macaddr,
g->rdctbl, B_TRUE)) != 0) {
return (rv);
}
mutex_enter(nxge->genlock);
g->n_mac_addrs++;
mutex_exit(nxge->genlock);
return (0);
}
static int
nxge_hio_set_unicst(void *arg, const uint8_t *macaddr)
{
p_nxge_t nxgep = (p_nxge_t)arg;
struct ether_addr addrp;
bcopy(macaddr, (uint8_t *)&addrp, ETHERADDRL);
if (nxge_set_mac_addr(nxgep, &addrp)) {
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"<== nxge_m_unicst: set unitcast failed"));
return (EINVAL);
}
nxgep->primary = B_TRUE;
return (0);
}
/*ARGSUSED*/
static int
nxge_hio_clear_unicst(p_nxge_t nxgep, const uint8_t *mac_addr)
{
nxgep->primary = B_FALSE;
return (0);
}
static int
nxge_hio_add_mac(void *arg, const uint8_t *mac_addr)
{
nxge_ring_group_t *group = (nxge_ring_group_t *)arg;
p_nxge_t nxge = group->nxgep;
int rv;
nxge_hio_vr_t *vr; /* The Virtualization Region */
ASSERT(group->type == MAC_RING_TYPE_RX);
mutex_enter(nxge->genlock);
if (!nxge->primary && group->port_default_grp) {
rv = nxge_hio_set_unicst((void *)nxge, mac_addr);
mutex_exit(nxge->genlock);
return (rv);
}
/*
* If the group is associated with a VR, then only one
* address may be assigned to the group.
*/
vr = (nxge_hio_vr_t *)nxge->shares[group->sindex].vrp;
if ((vr != NULL) && (group->n_mac_addrs)) {
mutex_exit(nxge->genlock);
return (ENOSPC);
}
mutex_exit(nxge->genlock);
/*
* Program the mac address for the group.
*/
if ((rv = nxge_hio_group_mac_add(nxge, group,
mac_addr)) != 0) {
return (rv);
}
return (0);
}
static int
find_mac_slot(nxge_mmac_t *mmac_info, const uint8_t *mac_addr)
{
int i;
for (i = 0; i <= mmac_info->num_mmac; i++) {
if (memcmp(mmac_info->mac_pool[i].addr, mac_addr,
ETHERADDRL) == 0) {
return (i);
}
}
return (-1);
}
/* ARGSUSED */
static int
nxge_hio_rem_mac(void *arg, const uint8_t *mac_addr)
{
nxge_ring_group_t *group = (nxge_ring_group_t *)arg;
struct ether_addr addrp;
p_nxge_t nxge = group->nxgep;
nxge_mmac_t *mmac_info;
int rv, slot;
ASSERT(group->type == MAC_RING_TYPE_RX);
mutex_enter(nxge->genlock);
mmac_info = &nxge->nxge_mmac_info;
slot = find_mac_slot(mmac_info, mac_addr);
if (slot < 0) {
if (group->port_default_grp && nxge->primary) {
bcopy(mac_addr, (uint8_t *)&addrp, ETHERADDRL);
if (ether_cmp(&addrp, &nxge->ouraddr) == 0) {
rv = nxge_hio_clear_unicst(nxge, mac_addr);
mutex_exit(nxge->genlock);
return (rv);
} else {
mutex_exit(nxge->genlock);
return (EINVAL);
}
} else {
mutex_exit(nxge->genlock);
return (EINVAL);
}
}
mutex_exit(nxge->genlock);
/*
* Remove the mac address for the group
*/
if ((rv = nxge_m_mmac_remove(nxge, slot)) != 0) {
return (rv);
}
mutex_enter(nxge->genlock);
group->n_mac_addrs--;
mutex_exit(nxge->genlock);
return (0);
}
static int
nxge_hio_group_start(mac_group_driver_t gdriver)
{
nxge_ring_group_t *group = (nxge_ring_group_t *)gdriver;
nxge_rdc_grp_t *rdc_grp_p;
int rdctbl;
int dev_gindex;
ASSERT(group->type == MAC_RING_TYPE_RX);
#ifdef later
ASSERT(group->nxgep->nxge_mac_state == NXGE_MAC_STARTED);
#endif
if (group->nxgep->nxge_mac_state != NXGE_MAC_STARTED)
return (ENXIO);
mutex_enter(group->nxgep->genlock);
dev_gindex = group->nxgep->pt_config.hw_config.def_mac_rxdma_grpid +
group->gindex;
rdc_grp_p = &group->nxgep->pt_config.rdc_grps[dev_gindex];
/*
* Get an rdc table for this group.
* Group ID is given by the caller, and that's the group it needs
* to bind to. The default group is already bound when the driver
* was attached.
*
* For Group 0, it's RDC table was allocated at attach time
* no need to allocate a new table.
*/
if (group->gindex != 0) {
rdctbl = nxge_fzc_rdc_tbl_bind(group->nxgep,
dev_gindex, B_TRUE);
if (rdctbl < 0) {
mutex_exit(group->nxgep->genlock);
return (rdctbl);
}
} else {
rdctbl = group->nxgep->pt_config.hw_config.def_mac_rxdma_grpid;
}
group->rdctbl = rdctbl;
(void) nxge_init_fzc_rdc_tbl(group->nxgep, rdc_grp_p, rdctbl);
group->started = B_TRUE;
mutex_exit(group->nxgep->genlock);
return (0);
}
static void
nxge_hio_group_stop(mac_group_driver_t gdriver)
{
nxge_ring_group_t *group = (nxge_ring_group_t *)gdriver;
ASSERT(group->type == MAC_RING_TYPE_RX);
mutex_enter(group->nxgep->genlock);
group->started = B_FALSE;
/*
* Unbind the RDC table previously bound for this group.
*
* Since RDC table for group 0 was allocated at attach
* time, no need to unbind the table here.
*/
if (group->gindex != 0)
(void) nxge_fzc_rdc_tbl_unbind(group->nxgep, group->rdctbl);
mutex_exit(group->nxgep->genlock);
}
/* ARGSUSED */
void
nxge_hio_group_get(void *arg, mac_ring_type_t type, int groupid,
mac_group_info_t *infop, mac_group_handle_t ghdl)
{
p_nxge_t nxgep = (p_nxge_t)arg;
nxge_ring_group_t *group;
int dev_gindex;
switch (type) {
case MAC_RING_TYPE_RX:
group = &nxgep->rx_hio_groups[groupid];
group->nxgep = nxgep;
group->ghandle = ghdl;
group->gindex = groupid;
group->sindex = 0; /* not yet bound to a share */
dev_gindex = nxgep->pt_config.hw_config.def_mac_rxdma_grpid +
groupid;
if (nxgep->pt_config.hw_config.def_mac_rxdma_grpid ==
dev_gindex)
group->port_default_grp = B_TRUE;
infop->mgi_driver = (mac_group_driver_t)group;
infop->mgi_start = nxge_hio_group_start;
infop->mgi_stop = nxge_hio_group_stop;
infop->mgi_addmac = nxge_hio_add_mac;
infop->mgi_remmac = nxge_hio_rem_mac;
infop->mgi_count =
nxgep->pt_config.rdc_grps[dev_gindex].max_rdcs;
break;
case MAC_RING_TYPE_TX:
/*
* 'groupid' for TX should be incremented by one since
* the default group (groupid 0) is not known by the MAC layer
*/
group = &nxgep->tx_hio_groups[groupid + 1];
group->nxgep = nxgep;
group->ghandle = ghdl;
group->gindex = groupid + 1;
group->sindex = 0; /* not yet bound to a share */
infop->mgi_driver = (mac_group_driver_t)group;
infop->mgi_start = NULL;
infop->mgi_stop = NULL;
infop->mgi_addmac = NULL; /* not needed */
infop->mgi_remmac = NULL; /* not needed */
/* no rings associated with group initially */
infop->mgi_count = 0;
break;
}
}
#if defined(sun4v)
int
nxge_hio_share_assign(
nxge_t *nxge,
uint64_t cookie,
res_map_t *tmap,
res_map_t *rmap,
nxge_hio_vr_t *vr)
{
nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
uint64_t slot, hv_rv;
nxge_hio_dc_t *dc;
nxhv_vr_fp_t *fp;
int i;
/*
* Ask the Hypervisor to set up the VR for us
*/
fp = &nhd->hio.vr;
if ((hv_rv = (*fp->assign)(vr->region, cookie, &vr->cookie))) {
NXGE_ERROR_MSG((nxge, HIO_CTL,
"nxge_hio_share_assign: "
"vr->assign() returned %d", hv_rv));
return (-EIO);
}
/*
* For each shared TDC, ask the HV to find us an empty slot.
* -----------------------------------------------------
*/
dc = vr->tx_group.dc;
for (i = 0; i < NXGE_MAX_TDCS; i++) {
nxhv_dc_fp_t *tx = &nhd->hio.tx;
while (dc) {
hv_rv = (*tx->assign)
(vr->cookie, dc->channel, &slot);
if (hv_rv != 0) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
"nxge_hio_share_assign: "
"tx->assign(%x, %d) failed: %ld",
vr->cookie, dc->channel, hv_rv));
return (-EIO);
}
dc->cookie = vr->cookie;
dc->page = (vp_channel_t)slot;
/* Inform the caller about the slot chosen. */
(*tmap) |= 1 << slot;
dc = dc->next;
}
}
/*
* For each shared RDC, ask the HV to find us an empty slot.
* -----------------------------------------------------
*/
dc = vr->rx_group.dc;
for (i = 0; i < NXGE_MAX_RDCS; i++) {
nxhv_dc_fp_t *rx = &nhd->hio.rx;
while (dc) {
hv_rv = (*rx->assign)
(vr->cookie, dc->channel, &slot);
if (hv_rv != 0) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
"nxge_hio_share_assign: "
"rx->assign(%x, %d) failed: %ld",
vr->cookie, dc->channel, hv_rv));
return (-EIO);
}
dc->cookie = vr->cookie;
dc->page = (vp_channel_t)slot;
/* Inform the caller about the slot chosen. */
(*rmap) |= 1 << slot;
dc = dc->next;
}
}
return (0);
}
void
nxge_hio_share_unassign(
nxge_hio_vr_t *vr)
{
nxge_t *nxge = (nxge_t *)vr->nxge;
nxge_hio_data_t *nhd;
nxge_hio_dc_t *dc;
nxhv_vr_fp_t *fp;
uint64_t hv_rv;
nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
dc = vr->tx_group.dc;
while (dc) {
nxhv_dc_fp_t *tx = &nhd->hio.tx;
hv_rv = (*tx->unassign)(vr->cookie, dc->page);
if (hv_rv != 0) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
"nxge_hio_share_unassign: "
"tx->unassign(%x, %d) failed: %ld",
vr->cookie, dc->page, hv_rv));
}
dc = dc->next;
}
dc = vr->rx_group.dc;
while (dc) {
nxhv_dc_fp_t *rx = &nhd->hio.rx;
hv_rv = (*rx->unassign)(vr->cookie, dc->page);
if (hv_rv != 0) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
"nxge_hio_share_unassign: "
"rx->unassign(%x, %d) failed: %ld",
vr->cookie, dc->page, hv_rv));
}
dc = dc->next;
}
fp = &nhd->hio.vr;
if (fp->unassign) {
hv_rv = (*fp->unassign)(vr->cookie);
if (hv_rv != 0) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
"nxge_hio_share_unassign: "
"vr->assign(%x) failed: %ld",
vr->cookie, hv_rv));
}
}
}
int
nxge_hio_share_alloc(void *arg, mac_share_handle_t *shandle)
{
p_nxge_t nxge = (p_nxge_t)arg;
nxge_share_handle_t *shp;
nxge_hio_vr_t *vr; /* The Virtualization Region */
nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_share"));
if (nhd->hio.vr.assign == 0 || nhd->hio.tx.assign == 0 ||
nhd->hio.rx.assign == 0) {
NXGE_ERROR_MSG((nxge, HIO_CTL, "HV assign function(s) NULL"));
return (EIO);
}
/*
* Get a VR.
*/
if ((vr = nxge_hio_vr_share(nxge)) == 0)
return (EAGAIN);
shp = &nxge->shares[vr->region];
shp->nxgep = nxge;
shp->index = vr->region;
shp->vrp = (void *)vr;
shp->tmap = shp->rmap = 0; /* to be assigned by ms_sbind */
shp->rxgroup = 0; /* to be assigned by ms_sadd */
shp->active = B_FALSE; /* not bound yet */
*shandle = (mac_share_handle_t)shp;
NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_share"));
return (0);
}
void
nxge_hio_share_free(mac_share_handle_t shandle)
{
nxge_share_handle_t *shp = (nxge_share_handle_t *)shandle;
nxge_hio_vr_t *vr;
/*
* Clear internal handle state.
*/
vr = shp->vrp;
shp->vrp = (void *)NULL;
shp->index = 0;
shp->tmap = 0;
shp->rmap = 0;
shp->rxgroup = 0;
shp->active = B_FALSE;
/*
* Free VR resource.
*/
nxge_hio_unshare(vr);
}
void
nxge_hio_share_query(mac_share_handle_t shandle, mac_ring_type_t type,
mac_ring_handle_t *rings, uint_t *n_rings)
{
nxge_t *nxge;
nxge_share_handle_t *shp = (nxge_share_handle_t *)shandle;
nxge_ring_handle_t *rh;
uint32_t offset;
nxge = shp->nxgep;
switch (type) {
case MAC_RING_TYPE_RX:
rh = nxge->rx_ring_handles;
offset = nxge->pt_config.hw_config.start_rdc;
break;
case MAC_RING_TYPE_TX:
rh = nxge->tx_ring_handles;
offset = nxge->pt_config.hw_config.tdc.start;
break;
}
/*
* In version 1.0, we may only give a VR 2 RDCs/TDCs. Not only that,
* but the HV has statically assigned the channels like so:
* VR0: RDC0 & RDC1
* VR1: RDC2 & RDC3, etc.
* The TDCs are assigned in exactly the same way.
*/
if (rings != NULL) {
rings[0] = rh[(shp->index * 2) - offset].ring_handle;
rings[1] = rh[(shp->index * 2 + 1) - offset].ring_handle;
}
if (n_rings != NULL) {
*n_rings = 2;
}
}
int
nxge_hio_share_add_group(mac_share_handle_t shandle,
mac_group_driver_t ghandle)
{
nxge_t *nxge;
nxge_share_handle_t *shp = (nxge_share_handle_t *)shandle;
nxge_ring_group_t *rg = (nxge_ring_group_t *)ghandle;
nxge_hio_vr_t *vr; /* The Virtualization Region */
nxge_grp_t *group;
int i;
if (rg->sindex != 0) {
/* the group is already bound to a share */
return (EALREADY);
}
/*
* If we are adding a group 0 to a share, this
* is not correct.
*/
ASSERT(rg->gindex != 0);
nxge = rg->nxgep;
vr = shp->vrp;
switch (rg->type) {
case MAC_RING_TYPE_RX:
/*
* Make sure that the group has the right rings associated
* for the share. In version 1.0, we may only give a VR
* 2 RDCs. Not only that, but the HV has statically
* assigned the channels like so:
* VR0: RDC0 & RDC1
* VR1: RDC2 & RDC3, etc.
*/
group = nxge->rx_set.group[rg->gindex];
if (group->count > 2) {
/* a share can have at most 2 rings */
return (EINVAL);
}
for (i = 0; i < NXGE_MAX_RDCS; i++) {
if (group->map & (1 << i)) {
if ((i != shp->index * 2) &&
(i != (shp->index * 2 + 1))) {
/*
* A group with invalid rings was
* attempted to bind to this share
*/
return (EINVAL);
}
}
}
rg->sindex = vr->region;
vr->rdc_tbl = rg->rdctbl;
shp->rxgroup = vr->rdc_tbl;
break;
case MAC_RING_TYPE_TX:
/*
* Make sure that the group has the right rings associated
* for the share. In version 1.0, we may only give a VR
* 2 TDCs. Not only that, but the HV has statically
* assigned the channels like so:
* VR0: TDC0 & TDC1
* VR1: TDC2 & TDC3, etc.
*/
group = nxge->tx_set.group[rg->gindex];
if (group->count > 2) {
/* a share can have at most 2 rings */
return (EINVAL);
}
for (i = 0; i < NXGE_MAX_TDCS; i++) {
if (group->map & (1 << i)) {
if ((i != shp->index * 2) &&
(i != (shp->index * 2 + 1))) {
/*
* A group with invalid rings was
* attempted to bind to this share
*/
return (EINVAL);
}
}
}
vr->tdc_tbl = nxge->pt_config.hw_config.def_mac_txdma_grpid +
rg->gindex;
rg->sindex = vr->region;
break;
}
return (0);
}
int
nxge_hio_share_rem_group(mac_share_handle_t shandle,
mac_group_driver_t ghandle)
{
nxge_share_handle_t *shp = (nxge_share_handle_t *)shandle;
nxge_ring_group_t *group = (nxge_ring_group_t *)ghandle;
nxge_hio_vr_t *vr; /* The Virtualization Region */
int rv = 0;
vr = shp->vrp;
switch (group->type) {
case MAC_RING_TYPE_RX:
group->sindex = 0;
vr->rdc_tbl = 0;
shp->rxgroup = 0;
break;
case MAC_RING_TYPE_TX:
group->sindex = 0;
vr->tdc_tbl = 0;
break;
}
return (rv);
}
int
nxge_hio_share_bind(mac_share_handle_t shandle, uint64_t cookie,
uint64_t *rcookie)
{
nxge_t *nxge;
nxge_share_handle_t *shp = (nxge_share_handle_t *)shandle;
nxge_hio_vr_t *vr;
uint64_t rmap, tmap, hv_rmap, hv_tmap;
int rv;
nxge = shp->nxgep;
vr = (nxge_hio_vr_t *)shp->vrp;
/*
* Add resources to the share.
* For each DMA channel associated with the VR, bind its resources
* to the VR.
*/
tmap = 0;
rv = nxge_hio_addres(vr, MAC_RING_TYPE_TX, &tmap);
if (rv != 0) {
return (rv);
}
rmap = 0;
rv = nxge_hio_addres(vr, MAC_RING_TYPE_RX, &rmap);
if (rv != 0) {
nxge_hio_remres(vr, MAC_RING_TYPE_TX, tmap);
return (rv);
}
/*
* Ask the Hypervisor to set up the VR and allocate slots for
* each rings associated with the VR.
*/
hv_tmap = hv_rmap = 0;
if ((rv = nxge_hio_share_assign(nxge, cookie,
&hv_tmap, &hv_rmap, vr))) {
nxge_hio_remres(vr, MAC_RING_TYPE_TX, tmap);
nxge_hio_remres(vr, MAC_RING_TYPE_RX, rmap);
return (rv);
}
shp->active = B_TRUE;
shp->tmap = hv_tmap;
shp->rmap = hv_rmap;
/* high 32 bits are cfg_hdl and low 32 bits are HV cookie */
*rcookie = (((uint64_t)nxge->niu_cfg_hdl) << 32) | vr->cookie;
return (0);
}
void
nxge_hio_share_unbind(mac_share_handle_t shandle)
{
nxge_share_handle_t *shp = (nxge_share_handle_t *)shandle;
/*
* First, unassign the VR (take it back),
* so we can enable interrupts again.
*/
nxge_hio_share_unassign(shp->vrp);
/*
* Free Ring Resources for TX and RX
*/
nxge_hio_remres(shp->vrp, MAC_RING_TYPE_TX, shp->tmap);
nxge_hio_remres(shp->vrp, MAC_RING_TYPE_RX, shp->rmap);
}
/*
* nxge_hio_vr_share
*
* Find an unused Virtualization Region (VR).
*
* Arguments:
* nxge
*
* Notes:
*
* Context:
* Service domain
*/
nxge_hio_vr_t *
nxge_hio_vr_share(
nxge_t *nxge)
{
nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
nxge_hio_vr_t *vr;
int first, limit, region;
NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_vr_share"));
MUTEX_ENTER(&nhd->lock);
if (nhd->vrs == 0) {
MUTEX_EXIT(&nhd->lock);
return (0);
}
/* Find an empty virtual region (VR). */
if (nxge->function_num == 0) {
// FUNC0_VIR0 'belongs' to NIU port 0.
first = FUNC0_VIR1;
limit = FUNC2_VIR0;
} else if (nxge->function_num == 1) {
// FUNC2_VIR0 'belongs' to NIU port 1.
first = FUNC2_VIR1;
limit = FUNC_VIR_MAX;
} else {
cmn_err(CE_WARN,
"Shares not supported on function(%d) at this time.\n",
nxge->function_num);
}
for (region = first; region < limit; region++) {
if (nhd->vr[region].nxge == 0)
break;
}
if (region == limit) {
MUTEX_EXIT(&nhd->lock);
return (0);
}
vr = &nhd->vr[region];
vr->nxge = (uintptr_t)nxge;
vr->region = (uintptr_t)region;
nhd->vrs--;
MUTEX_EXIT(&nhd->lock);
NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_vr_share"));
return (vr);
}
void
nxge_hio_unshare(
nxge_hio_vr_t *vr)
{
nxge_t *nxge = (nxge_t *)vr->nxge;
nxge_hio_data_t *nhd;
vr_region_t region;
NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_unshare"));
if (!nxge) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_unshare: "
"vr->nxge is NULL"));
return;
}
/*
* This function is no longer called, but I will keep it
* here in case we want to revisit this topic in the future.
*
* nxge_hio_hostinfo_uninit(nxge, vr);
*/
/*
* XXX: This is done by ms_sremove?
* (void) nxge_fzc_rdc_tbl_unbind(nxge, vr->rdc_tbl);
*/
nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
MUTEX_ENTER(&nhd->lock);
region = vr->region;
(void) memset(vr, 0, sizeof (*vr));
vr->region = region;
nhd->vrs++;
MUTEX_EXIT(&nhd->lock);
NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_unshare"));
}
int
nxge_hio_addres(
nxge_hio_vr_t *vr,
mac_ring_type_t type,
uint64_t *map)
{
nxge_t *nxge = (nxge_t *)vr->nxge;
nxge_grp_t *group;
int groupid;
int i;
int max_dcs;
NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_addres"));
if (!nxge)
return (EINVAL);
/*
* For each ring associated with the group, add the resources
* to the group and bind.
*/
max_dcs = (type == MAC_RING_TYPE_TX) ? NXGE_MAX_TDCS : NXGE_MAX_RDCS;
if (type == MAC_RING_TYPE_TX) {
/* set->group is an array of group indexed by a port group id */
groupid = vr->tdc_tbl -
nxge->pt_config.hw_config.def_mac_txdma_grpid;
group = nxge->tx_set.group[groupid];
} else {
/* set->group is an array of group indexed by a port group id */
groupid = vr->rdc_tbl -
nxge->pt_config.hw_config.def_mac_rxdma_grpid;
group = nxge->rx_set.group[groupid];
}
if (group->map == 0) {
NXGE_DEBUG_MSG((nxge, HIO_CTL, "There is no rings associated "
"with this VR"));
return (EINVAL);
}
for (i = 0; i < max_dcs; i++) {
if (group->map & (1 << i)) {
int rv;
if ((rv = nxge_hio_dc_share(nxge, vr, type, i)) < 0) {
if (*map == 0) /* Couldn't get even one DC. */
return (-rv);
else
break;
}
*map |= (1 << i);
}
}
NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_addres"));
return (0);
}
/* ARGSUSED */
void
nxge_hio_remres(
nxge_hio_vr_t *vr,
mac_ring_type_t type,
res_map_t res_map)
{
nxge_t *nxge = (nxge_t *)vr->nxge;
nxge_grp_t *group;
if (!nxge) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_remres: "
"vr->nxge is NULL"));
return;
}
NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_remres(%lx)", res_map));
/*
* For each ring bound to the group, remove the DMA resources
* from the group and unbind.
*/
group = (type == MAC_RING_TYPE_TX ? &vr->tx_group : &vr->rx_group);
while (group->dc) {
nxge_hio_dc_t *dc = group->dc;
NXGE_DC_RESET(res_map, dc->page);
nxge_hio_dc_unshare(nxge, vr, type, dc->channel);
}
if (res_map) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_remres: "
"res_map %lx", res_map));
}
NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_remres"));
}
/*
* nxge_hio_tdc_share
*
* Share an unused TDC channel.
*
* Arguments:
* nxge
*
* Notes:
*
* A.7.3 Reconfigure Tx DMA channel
* Disable TxDMA A.9.6.10
* [Rebind TxDMA channel to Port A.9.6.7]
*
* We don't have to Rebind the TDC to the port - it always already bound.
*
* Soft Reset TxDMA A.9.6.2
*
* This procedure will be executed by nxge_init_txdma_channel() in the
* guest domain:
*
* Re-initialize TxDMA A.9.6.8
* Reconfigure TxDMA
* Enable TxDMA A.9.6.9
*
* Context:
* Service domain
*/
int
nxge_hio_tdc_share(
nxge_t *nxge,
int channel)
{
nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
nxge_grp_set_t *set = &nxge->tx_set;
tx_ring_t *ring;
int count;
NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_tdc_share"));
/*
* Wait until this channel is idle.
*/
ring = nxge->tx_rings->rings[channel];
(void) atomic_swap_32(&ring->tx_ring_offline, NXGE_TX_RING_OFFLINING);
if (ring->tx_ring_busy) {
/*
* Wait for 30 seconds.
*/
for (count = 30 * 1000; count; count--) {
if (ring->tx_ring_offline & NXGE_TX_RING_OFFLINED) {
break;
}
drv_usecwait(1000);
}
if (count == 0) {
(void) atomic_swap_32(&ring->tx_ring_offline,
NXGE_TX_RING_ONLINE);
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
"nxge_hio_tdc_share: "
"Tx ring %d was always BUSY", channel));
return (-EIO);
}
} else {
(void) atomic_swap_32(&ring->tx_ring_offline,
NXGE_TX_RING_OFFLINED);
}
MUTEX_ENTER(&nhd->lock);
nxge->tdc_is_shared[channel] = B_TRUE;
MUTEX_EXIT(&nhd->lock);
if (nxge_intr_remove(nxge, VP_BOUND_TX, channel) != NXGE_OK) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_tdc_share: "
"Failed to remove interrupt for TxDMA channel %d",
channel));
return (-EINVAL);
}
/* Disable TxDMA A.9.6.10 */
(void) nxge_txdma_channel_disable(nxge, channel);
/* The SD is sharing this channel. */
NXGE_DC_SET(set->shared.map, channel);
set->shared.count++;
/* Soft Reset TxDMA A.9.6.2 */
nxge_grp_dc_remove(nxge, VP_BOUND_TX, channel);
/*
* Initialize the DC-specific FZC control registers.
* -----------------------------------------------------
*/
if (nxge_init_fzc_tdc(nxge, channel) != NXGE_OK) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
"nxge_hio_tdc_share: FZC TDC failed: %d", channel));
return (-EIO);
}
NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_tdc_share"));
return (0);
}
/*
* nxge_hio_rdc_share
*
* Share an unused RDC channel.
*
* Arguments:
* nxge
*
* Notes:
*
* This is the latest version of the procedure to
* Reconfigure an Rx DMA channel:
*
* A.6.3 Reconfigure Rx DMA channel
* Stop RxMAC A.9.2.6
* Drain IPP Port A.9.3.6
* Stop and reset RxDMA A.9.5.3
*
* This procedure will be executed by nxge_init_rxdma_channel() in the
* guest domain:
*
* Initialize RxDMA A.9.5.4
* Reconfigure RxDMA
* Enable RxDMA A.9.5.5
*
* We will do this here, since the RDC is a canalis non grata:
* Enable RxMAC A.9.2.10
*
* Context:
* Service domain
*/
int
nxge_hio_rdc_share(
nxge_t *nxge,
nxge_hio_vr_t *vr,
int channel)
{
nxge_grp_set_t *set = &nxge->rx_set;
nxge_rdc_grp_t *rdc_grp;
NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_rdc_share"));
/* Disable interrupts. */
if (nxge_intr_remove(nxge, VP_BOUND_RX, channel) != NXGE_OK) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_share: "
"Failed to remove interrupt for RxDMA channel %d",
channel));
return (NXGE_ERROR);
}
/* Stop RxMAC = A.9.2.6 */
if (nxge_rx_mac_disable(nxge) != NXGE_OK) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_share: "
"Failed to disable RxMAC"));
}
/* Drain IPP Port = A.9.3.6 */
(void) nxge_ipp_drain(nxge);
/* Stop and reset RxDMA = A.9.5.3 */
// De-assert EN: RXDMA_CFIG1[31] = 0 (DMC+00000 )
if (nxge_disable_rxdma_channel(nxge, channel) != NXGE_OK) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_share: "
"Failed to disable RxDMA channel %d", channel));
}
/* The SD is sharing this channel. */
NXGE_DC_SET(set->shared.map, channel);
set->shared.count++;
// Assert RST: RXDMA_CFIG1[30] = 1
nxge_grp_dc_remove(nxge, VP_BOUND_RX, channel);
/*
* The guest domain will reconfigure the RDC later.
*
* But in the meantime, we must re-enable the Rx MAC so
* that we can start receiving packets again on the
* remaining RDCs:
*
* Enable RxMAC = A.9.2.10
*/
if (nxge_rx_mac_enable(nxge) != NXGE_OK) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
"nxge_hio_rdc_share: Rx MAC still disabled"));
}
/*
* Initialize the DC-specific FZC control registers.
* -----------------------------------------------------
*/
if (nxge_init_fzc_rdc(nxge, channel) != NXGE_OK) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
"nxge_hio_rdc_share: RZC RDC failed: %ld", channel));
return (-EIO);
}
/*
* Update the RDC group.
*/
rdc_grp = &nxge->pt_config.rdc_grps[vr->rdc_tbl];
NXGE_DC_SET(rdc_grp->map, channel);
NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_rdc_share"));
return (0);
}
/*
* nxge_hio_dc_share
*
* Share a DMA channel with a guest domain.
*
* Arguments:
* nxge
* vr The VR that <channel> will belong to.
* type Tx or Rx.
* channel Channel to share
*
* Notes:
*
* Context:
* Service domain
*/
int
nxge_hio_dc_share(
nxge_t *nxge,
nxge_hio_vr_t *vr,
mac_ring_type_t type,
int channel)
{
nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
nxge_hio_dc_t *dc;
nxge_grp_t *group;
int slot;
NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_dc_share(%cdc %d",
type == MAC_RING_TYPE_TX ? 't' : 'r', channel));
/* -------------------------------------------------- */
slot = (type == MAC_RING_TYPE_TX) ?
nxge_hio_tdc_share(nxge, channel) :
nxge_hio_rdc_share(nxge, vr, channel);
if (slot < 0) {
if (type == MAC_RING_TYPE_RX) {
nxge_hio_rdc_unshare(nxge, vr->rdc_tbl, channel);
} else {
nxge_hio_tdc_unshare(nxge, vr->tdc_tbl, channel);
}
return (slot);
}
MUTEX_ENTER(&nhd->lock);
/*
* Tag this channel.
* --------------------------------------------------
*/
dc = type == MAC_RING_TYPE_TX ? &nhd->tdc[channel] : &nhd->rdc[channel];
dc->vr = vr;
dc->channel = (nxge_channel_t)channel;
MUTEX_EXIT(&nhd->lock);
/*
* vr->[t|r]x_group is used by the service domain to
* keep track of its shared DMA channels.
*/
MUTEX_ENTER(&nxge->group_lock);
group = (type == MAC_RING_TYPE_TX ? &vr->tx_group : &vr->rx_group);
dc->group = group;
/* Initialize <group>, if necessary */
if (group->count == 0) {
group->nxge = nxge;
group->type = (type == MAC_RING_TYPE_TX) ?
VP_BOUND_TX : VP_BOUND_RX;
group->sequence = nhd->sequence++;
group->active = B_TRUE;
}
MUTEX_EXIT(&nxge->group_lock);
NXGE_ERROR_MSG((nxge, HIO_CTL,
"DC share: %cDC %d was assigned to slot %d",
type == MAC_RING_TYPE_TX ? 'T' : 'R', channel, slot));
nxge_grp_dc_append(nxge, group, dc);
NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_dc_share"));
return (0);
}
/*
* nxge_hio_tdc_unshare
*
* Unshare a TDC.
*
* Arguments:
* nxge
* channel The channel to unshare (add again).
*
* Notes:
*
* Context:
* Service domain
*/
void
nxge_hio_tdc_unshare(
nxge_t *nxge,
int dev_grpid,
int channel)
{
nxge_grp_set_t *set = &nxge->tx_set;
nxge_grp_t *group;
int grpid;
NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_tdc_unshare"));
NXGE_DC_RESET(set->shared.map, channel);
set->shared.count--;
grpid = dev_grpid - nxge->pt_config.hw_config.def_mac_txdma_grpid;
group = set->group[grpid];
if ((nxge_grp_dc_add(nxge, group, VP_BOUND_TX, channel))) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_tdc_unshare: "
"Failed to initialize TxDMA channel %d", channel));
return;
}
/* Re-add this interrupt. */
if (nxge_intr_add(nxge, VP_BOUND_TX, channel) != NXGE_OK) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_tdc_unshare: "
"Failed to add interrupt for TxDMA channel %d", channel));
}
NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_tdc_unshare"));
}
/*
* nxge_hio_rdc_unshare
*
* Unshare an RDC: add it to the SD's RDC groups (tables).
*
* Arguments:
* nxge
* channel The channel to unshare (add again).
*
* Notes:
*
* Context:
* Service domain
*/
void
nxge_hio_rdc_unshare(
nxge_t *nxge,
int dev_grpid,
int channel)
{
nxge_grp_set_t *set = &nxge->rx_set;
nxge_grp_t *group;
int grpid;
NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_rdc_unshare"));
/* Stop RxMAC = A.9.2.6 */
if (nxge_rx_mac_disable(nxge) != NXGE_OK) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_unshare: "
"Failed to disable RxMAC"));
}
/* Drain IPP Port = A.9.3.6 */
(void) nxge_ipp_drain(nxge);
/* Stop and reset RxDMA = A.9.5.3 */
// De-assert EN: RXDMA_CFIG1[31] = 0 (DMC+00000 )
if (nxge_disable_rxdma_channel(nxge, channel) != NXGE_OK) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_unshare: "
"Failed to disable RxDMA channel %d", channel));
}
NXGE_DC_RESET(set->shared.map, channel);
set->shared.count--;
grpid = dev_grpid - nxge->pt_config.hw_config.def_mac_rxdma_grpid;
group = set->group[grpid];
/*
* Assert RST: RXDMA_CFIG1[30] = 1
*
* Initialize RxDMA A.9.5.4
* Reconfigure RxDMA
* Enable RxDMA A.9.5.5
*/
if ((nxge_grp_dc_add(nxge, group, VP_BOUND_RX, channel))) {
/* Be sure to re-enable the RX MAC. */
if (nxge_rx_mac_enable(nxge) != NXGE_OK) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
"nxge_hio_rdc_share: Rx MAC still disabled"));
}
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_unshare: "
"Failed to initialize RxDMA channel %d", channel));
return;
}
/*
* Enable RxMAC = A.9.2.10
*/
if (nxge_rx_mac_enable(nxge) != NXGE_OK) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
"nxge_hio_rdc_share: Rx MAC still disabled"));
return;
}
/* Re-add this interrupt. */
if (nxge_intr_add(nxge, VP_BOUND_RX, channel) != NXGE_OK) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
"nxge_hio_rdc_unshare: Failed to add interrupt for "
"RxDMA CHANNEL %d", channel));
}
NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_rdc_unshare"));
}
/*
* nxge_hio_dc_unshare
*
* Unshare (reuse) a DMA channel.
*
* Arguments:
* nxge
* vr The VR that <channel> belongs to.
* type Tx or Rx.
* channel The DMA channel to reuse.
*
* Notes:
*
* Context:
* Service domain
*/
void
nxge_hio_dc_unshare(
nxge_t *nxge,
nxge_hio_vr_t *vr,
mac_ring_type_t type,
int channel)
{
nxge_grp_t *group;
nxge_hio_dc_t *dc;
NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_dc_unshare(%cdc %d)",
type == MAC_RING_TYPE_TX ? 't' : 'r', channel));
/* Unlink the channel from its group. */
/* -------------------------------------------------- */
group = (type == MAC_RING_TYPE_TX) ? &vr->tx_group : &vr->rx_group;
NXGE_DC_RESET(group->map, channel);
if ((dc = nxge_grp_dc_unlink(nxge, group, channel)) == 0) {
NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
"nxge_hio_dc_unshare(%d) failed", channel));
return;
}
dc->vr = 0;
dc->cookie = 0;
if (type == MAC_RING_TYPE_RX) {
nxge_hio_rdc_unshare(nxge, vr->rdc_tbl, channel);
} else {
nxge_hio_tdc_unshare(nxge, vr->tdc_tbl, channel);
}
NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_dc_unshare"));
}
/*
* nxge_hio_rxdma_bind_intr():
*
* For the guest domain driver, need to bind the interrupt group
* and state to the rx_rcr_ring_t.
*/
int
nxge_hio_rxdma_bind_intr(nxge_t *nxge, rx_rcr_ring_t *ring, int channel)
{
nxge_hio_dc_t *dc;
nxge_ldgv_t *control;
nxge_ldg_t *group;
nxge_ldv_t *device;
/*
* Find the DMA channel.
*/
if (!(dc = nxge_grp_dc_find(nxge, VP_BOUND_RX, channel))) {
return (NXGE_ERROR);
}
/*
* Get the control structure.
*/
control = nxge->ldgvp;
if (control == NULL) {
return (NXGE_ERROR);
}
group = &control->ldgp[dc->ldg.vector];
device = &control->ldvp[dc->ldg.ldsv];
MUTEX_ENTER(&ring->lock);
ring->ldgp = group;
ring->ldvp = device;
MUTEX_EXIT(&ring->lock);
return (NXGE_OK);
}
#endif /* if defined(sun4v) */