| /* |
| * 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 2010 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> |
| |
| /* |
| * 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); |
| extern int nxge_rx_ring_start(mac_ring_driver_t rdriver, uint64_t mr_gen_num); |
| |
| /* 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 == NULL) { |
| nhd = KMEM_ZALLOC(sizeof (*nhd), KM_SLEEP); |
| MUTEX_INIT(&nhd->lock, NULL, MUTEX_DRIVER, NULL); |
| nhd->type = NXGE_HIO_TYPE_SERVICE; |
| 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; |
| |
| if (group == NULL) |
| return; |
| |
| 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; |
| int error = 0; |
| |
| 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)) { |
| error = nxge_hio_ldsv_add(nxge, dc); |
| if (error != 0) { |
| MUTEX_EXIT(&nhd->lock); |
| return (NXGE_ERROR); |
| } |
| } |
| |
| 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 (¤t[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); |
| if (isLDOMguest(nxge)) |
| nhd->type = NXGE_HIO_TYPE_GUEST; |
| else |
| nhd->type = NXGE_HIO_TYPE_SERVICE; |
| 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; |
| /* |
| * Versions supported now are: |
| * - major number >= 1 (NIU_MAJOR_VER). |
| */ |
| if ((niu_hsvc->hsvc_major >= NIU_MAJOR_VER) || |
| (niu_hsvc->hsvc_major == 1 && |
| niu_hsvc->hsvc_minor == 1)) { |
| nxge->environs = SOLARIS_SERVICE_DOMAIN; |
| NXGE_ERROR_MSG((nxge, NXGE_ERR_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); |
| } |
| |
| 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); |
| ASSERT(group->nxgep != NULL); |
| |
| if (isLDOMguest(group->nxgep)) |
| return (0); |
| |
| 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); |
| ASSERT(group->nxgep != NULL); |
| |
| if (isLDOMguest(group->nxgep)) |
| return (0); |
| |
| 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); |
| ASSERT(group->nxgep != NULL); |
| |
| ASSERT(group->nxgep->nxge_mac_state == NXGE_MAC_STARTED); |
| if (group->nxgep->nxge_mac_state != NXGE_MAC_STARTED) |
| return (ENXIO); |
| |
| mutex_enter(group->nxgep->genlock); |
| if (isLDOMguest(group->nxgep)) |
| goto nxge_hio_group_start_exit; |
| |
| 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); |
| |
| nxge_hio_group_start_exit: |
| 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; |
| |
| if (isLDOMguest(group->nxgep)) |
| goto nxge_hio_group_stop_exit; |
| |
| /* |
| * 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); |
| |
| nxge_hio_group_stop_exit: |
| 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 */ |
| |
| if (!isLDOMguest(nxgep)) { |
| 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_count = |
| nxgep->pt_config.rdc_grps[dev_gindex].max_rdcs; |
| } else { |
| infop->mgi_count = NXGE_HIO_SHARE_MAX_CHANNELS; |
| } |
| |
| 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; |
| 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; |
| uint64_t major; |
| |
| /* |
| * Ask the Hypervisor to set up the VR for us |
| */ |
| fp = &nhd->hio.vr; |
| major = nxge->niu_hsvc.hsvc_major; |
| switch (major) { |
| case NIU_MAJOR_VER: /* 1 */ |
| if ((hv_rv = (*fp->assign)(vr->region, cookie, &vr->cookie))) { |
| NXGE_ERROR_MSG((nxge, HIO_CTL, |
| "nxge_hio_share_assign: major %d " |
| "vr->assign() returned %d", major, hv_rv)); |
| nxge_hio_unshare(vr); |
| return (-EIO); |
| } |
| |
| break; |
| |
| case NIU_MAJOR_VER_2: /* 2 */ |
| default: |
| if ((hv_rv = (*fp->cfgh_assign) |
| (nxge->niu_cfg_hdl, vr->region, cookie, &vr->cookie))) { |
| NXGE_ERROR_MSG((nxge, HIO_CTL, |
| "nxge_hio_share_assign: major %d " |
| "vr->assign() returned %d", major, hv_rv)); |
| nxge_hio_unshare(vr); |
| return (-EIO); |
| } |
| |
| break; |
| } |
| |
| NXGE_DEBUG_MSG((nxge, HIO_CTL, |
| "nxge_hio_share_assign: major %d " |
| "vr->assign() success", major)); |
| |
| /* |
| * 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; |
| |
| ASSERT(shp != NULL); |
| ASSERT(shp->nxgep != NULL); |
| ASSERT(shp->vrp != NULL); |
| |
| 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_grp_t *group; |
| int groupid; |
| int i, rv = 0; |
| int max_dcs; |
| |
| ASSERT(vr != NULL); |
| ASSERT(vr->nxge != NULL); |
| nxge = (nxge_t *)vr->nxge; |
| |
| NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_addres")); |
| |
| /* |
| * 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]; |
| } |
| |
| ASSERT(group != NULL); |
| |
| 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)) { |
| 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); |
| } |
| } |
| |
| if ((*map == 0) || (rv != 0)) { |
| NXGE_DEBUG_MSG((nxge, HIO_CTL, |
| "<== nxge_hio_addres: rv(%x)", rv)); |
| return (EIO); |
| } |
| |
| 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]; |
| ASSERT(ring != NULL); |
| |
| (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; |
| int i; |
| |
| 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")); |
| |
| for (i = 0; i < NXGE_MAX_RDCS; i++) { |
| if (nxge->rx_ring_handles[i].channel == channel) { |
| (void) nxge_rx_ring_start( |
| (mac_ring_driver_t)&nxge->rx_ring_handles[i], |
| nxge->rx_ring_handles[i].ring_gen_num); |
| } |
| } |
| } |
| |
| /* |
| * 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) */ |