Print this page
NEX-1890 update oce from source provided by Emulex
@@ -17,21 +17,27 @@
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
-/* Copyright © 2003-2011 Emulex. All rights reserved. */
+/*
+ * Copyright (c) 2009-2012 Emulex. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
/*
* Source file interrupt registration
* and related helper functions
*/
#include <oce_impl.h>
-
static uint_t oce_isr(caddr_t arg1, caddr_t arg2);
+static int
+oce_adjust_intrs(struct oce_dev *dev, ddi_cb_action_t action, int count);
/*
* top level function to setup interrupts
*
* dev - software handle to the device
@@ -39,59 +45,71 @@
* return DDI_SUCCESS => success, failure otherwise
*/
int
oce_setup_intr(struct oce_dev *dev)
{
- int ret;
+ int ret, i;
int intr_types = 0;
int navail = 0;
int nsupported = 0;
int min = 0;
int nreqd = 0;
int nallocd = 0;
+ extern int oce_irm_enable;
/* get supported intr types */
ret = ddi_intr_get_supported_types(dev->dip, &intr_types);
if (ret != DDI_SUCCESS) {
- oce_log(dev, CE_WARN, MOD_CONFIG, "%s",
- "Failed to retrieve intr types ");
+ oce_log(dev, CE_WARN, MOD_CONFIG,
+ "Failed to retrieve intr types 0x%x", ret);
return (DDI_FAILURE);
}
+ dev->rx_rings = min(dev->rx_rings, ncpus + dev->rss_cnt);
+ dev->tx_rings = min(dev->tx_rings, ncpus);
+#ifdef __sparc
+ nreqd = min(dev->tx_rings + dev->rx_rings - dev->rss_cnt, ncpus);
+ dev->rx_group[0].eq_idx = dev->tx_rings;
+#else
+ nreqd = max(dev->tx_rings, dev->rx_rings - dev->rss_cnt);
+#endif
+ min = OCE_MIN_VECTORS;
+
retry_intr:
+
if (intr_types & DDI_INTR_TYPE_MSIX) {
dev->intr_type = DDI_INTR_TYPE_MSIX;
- /* one vector is shared by MCC and Tx */
- nreqd = dev->rx_rings + 1;
- min = OCE_MIN_VECTORS;
- } else if (intr_types & DDI_INTR_TYPE_FIXED) {
+ } else {
+ oce_log(dev, CE_WARN, MOD_CONFIG, "%s",
+ "MSIX not available");
+
+ if (intr_types & DDI_INTR_TYPE_FIXED) {
dev->intr_type = DDI_INTR_TYPE_FIXED;
+ dev->rx_rings = dev->tx_rings = min;
+ } else {
+ return (DDI_FAILURE);
+ }
nreqd = OCE_MIN_VECTORS;
- min = OCE_MIN_VECTORS;
}
ret = ddi_intr_get_nintrs(dev->dip, dev->intr_type, &nsupported);
if (ret != DDI_SUCCESS) {
oce_log(dev, CE_WARN, MOD_CONFIG,
- "Could not get nintrs:0x%d", ret);
+ "Could not get supported intrs:0x%x", ret);
return (DDI_FAILURE);
}
/* get the number of vectors available */
ret = ddi_intr_get_navail(dev->dip, dev->intr_type, &navail);
if (ret != DDI_SUCCESS || navail < min) {
- oce_log(dev, CE_WARN, MOD_CONFIG,
- "Could not get msix vectors:0x%x",
- navail);
- return (DDI_FAILURE);
+ oce_log(dev, CE_NOTE, MOD_CONFIG,
+ "Vectors: supported:0x%x, available:0x%x, ret:0x%x",
+ nsupported, navail, ret);
+ intr_types &= ~dev->intr_type;
+ goto retry_intr;
}
- if (navail < min) {
- return (DDI_FAILURE);
- }
-
- /* if the requested number is more than available reset reqd */
if (navail < nreqd) {
nreqd = navail;
}
/* allocate htable */
@@ -99,57 +117,58 @@
dev->htable = kmem_zalloc(dev->hsize, KM_NOSLEEP);
if (dev->htable == NULL)
return (DDI_FAILURE);
- nallocd = 0;
- /* allocate interrupt handlers */
+ /* allocate interrupt */
ret = ddi_intr_alloc(dev->dip, dev->htable, dev->intr_type,
0, nreqd, &nallocd, DDI_INTR_ALLOC_NORMAL);
- if (ret != DDI_SUCCESS) {
- goto fail_intr;
+ if (ret != DDI_SUCCESS || nallocd < min) {
+ oce_log(dev, CE_WARN, MOD_CONFIG,
+ "Alloc intr failed: %d %d",
+ navail, ret);
+ kmem_free(dev->htable, nreqd * sizeof (ddi_intr_handle_t));
+ intr_types &= ~dev->intr_type;
+ goto retry_intr;
}
- dev->num_vectors = nallocd;
- if (nallocd < min) {
- goto fail_intr;
- }
-
/*
* get the interrupt priority. Assumption is that all handlers have
* equal priority
*/
ret = ddi_intr_get_pri(dev->htable[0], &dev->intr_pri);
if (ret != DDI_SUCCESS) {
- goto fail_intr;
+ oce_log(dev, CE_WARN, MOD_CONFIG,
+ "Unable to get intr priority: 0x%x", ret);
+
+ for (i = 0; i < dev->num_vectors; i++) {
+ (void) ddi_intr_free(dev->htable[i]);
}
+ kmem_free(dev->htable, nreqd * sizeof (ddi_intr_handle_t));
+ return (DDI_FAILURE);
+ }
(void) ddi_intr_get_cap(dev->htable[0], &dev->intr_cap);
- if ((intr_types & DDI_INTR_TYPE_MSIX) && (nallocd > 1)) {
- dev->rx_rings = nallocd - 1;
+ /* update the actual number of interrupts allocated */
+ dev->num_vectors = nallocd;
+ if (oce_irm_enable && dev->intr_type == DDI_INTR_TYPE_MSIX) {
+ dev->max_vectors = nreqd;
} else {
- dev->rx_rings = 1;
+ dev->max_vectors = nallocd;
+ dev->tx_rings = min(dev->tx_rings, nallocd);
+ dev->rx_rings = min(dev->rx_rings, nallocd + dev->rss_cnt);
}
+ oce_group_rings(dev);
return (DDI_SUCCESS);
-
-fail_intr:
- (void) oce_teardown_intr(dev);
- if ((dev->intr_type == DDI_INTR_TYPE_MSIX) &&
- (intr_types & DDI_INTR_TYPE_FIXED)) {
- intr_types &= ~DDI_INTR_TYPE_MSIX;
- oce_log(dev, CE_NOTE, MOD_CONFIG, "%s",
- "Could not get MSIX vectors, trying for FIXED vectors");
- goto retry_intr;
- }
- return (DDI_FAILURE);
}
+
/*
* top level function to undo initialization in oce_setup_intr
*
* dev - software handle to the device
*
@@ -166,11 +185,13 @@
}
/* release htable */
kmem_free(dev->htable, dev->hsize);
dev->htable = NULL;
-
+ if (dev->attach_state & ATTACH_CB_REG) {
+ (void) ddi_cb_unregister(dev->cb_handle);
+ }
return (DDI_SUCCESS);
}
/*
* helper function to add ISR based on interrupt type
@@ -184,14 +205,15 @@
{
int i = 0;
int ret;
for (i = 0; i < dev->num_vectors; i++) {
ret = ddi_intr_add_handler(dev->htable[i], oce_isr,
- (caddr_t)dev->eq[i], NULL);
+ (caddr_t)&dev->eq[i], NULL);
if (ret != DDI_SUCCESS) {
- oce_log(dev, CE_WARN, MOD_CONFIG, "%s",
- "Failed to add interrupt handlers");
+ oce_log(dev, CE_WARN, MOD_CONFIG,
+ "Failed to add interrupt handler %d, ret = 0x%x",
+ i, ret);
for (i--; i >= 0; i--) {
(void) ddi_intr_remove_handler(dev->htable[i]);
}
return (DDI_FAILURE);
}
@@ -230,30 +252,40 @@
*
* dev - software handle to the device
*
* return DDI_SUCCESS => success, failure otherwise
*/
-void
+int
oce_ei(struct oce_dev *dev)
{
int i;
int ret;
if (dev->intr_cap & DDI_INTR_FLAG_BLOCK) {
- (void) ddi_intr_block_enable(dev->htable, dev->num_vectors);
+ ret = ddi_intr_block_enable(dev->htable, dev->num_vectors);
+ if (ret != DDI_SUCCESS) {
+ oce_log(dev, CE_WARN, MOD_CONFIG,
+ "Interrupts block enable failed :%d\n", ret);
+ return (DDI_FAILURE);
+ }
} else {
-
for (i = 0; i < dev->num_vectors; i++) {
ret = ddi_intr_enable(dev->htable[i]);
if (ret != DDI_SUCCESS) {
+ oce_log(dev, CE_WARN, MOD_CONFIG,
+ "Failed to enable, ret %d, interrupt %d,"
+ " type %d, cnt %d ",
+ ret, i, dev->intr_type, dev->num_vectors);
for (i--; i >= 0; i--) {
(void) ddi_intr_disable(dev->htable[i]);
}
+ return (DDI_FAILURE);
}
}
}
- oce_chip_ei(dev);
+
+ return (DDI_SUCCESS);
} /* oce_ei */
void
oce_chip_di(struct oce_dev *dev)
{
@@ -269,85 +301,297 @@
*
* dev - software handle to the device
*
* return DDI_SUCCESS => success, failure otherwise
*/
-void
+int
oce_di(struct oce_dev *dev)
{
int i;
int ret;
+ dev->state &= ~STATE_INTR_ENABLED;
+ if (!LANCER_CHIP(dev))
oce_chip_di(dev);
+
if (dev->intr_cap & DDI_INTR_FLAG_BLOCK) {
- (void) ddi_intr_block_disable(dev->htable, dev->num_vectors);
+ ret = ddi_intr_block_disable(dev->htable, dev->num_vectors);
+ if (ret != DDI_SUCCESS) {
+ oce_log(dev, CE_WARN, MOD_CONFIG,
+ "Interrupt block disable failed :%d\n", ret);
+ return (DDI_FAILURE);
+ }
} else {
for (i = 0; i < dev->num_vectors; i++) {
ret = ddi_intr_disable(dev->htable[i]);
if (ret != DDI_SUCCESS) {
oce_log(dev, CE_WARN, MOD_CONFIG,
- "Failed to disable interrupts 0x%x", ret);
+ "Failed to disable the interrupts 0x%x",
+ ret);
+ return (DDI_FAILURE);
}
}
}
-
+ return (DDI_SUCCESS);
} /* oce_di */
-/*
- * command interrupt handler routine added to all vectors
- *
- * arg1 = callback data
- * arg2 - callback data
- *
- * return DDI_INTR_CLAIMED => interrupt was claimed by the ISR
- */
-static uint_t
-oce_isr(caddr_t arg1, caddr_t arg2)
+
+int
+oce_ring_intr_enable(mac_intr_handle_t ring_handle)
{
- struct oce_eq *eq;
+ struct oce_rq *rx_ring = (struct oce_rq *)ring_handle;
+ struct oce_dev *dev;
+ dev = rx_ring->parent;
+ oce_group_t *grp = rx_ring->grp;
+ mutex_enter(&grp->grp_lock);
+ if (grp->state & GROUP_SUSPEND) {
+ mutex_exit(&grp->grp_lock);
+ return (DDI_SUCCESS);
+ }
+ mutex_enter(&rx_ring->rx_lock);
+ oce_arm_cq(dev, rx_ring->cq->cq_id, 0, B_TRUE);
+ rx_ring->qmode = OCE_MODE_INTR;
+ mutex_exit(&rx_ring->rx_lock);
+ mutex_exit(&grp->grp_lock);
+ return (DDI_SUCCESS);
+}
+
+
+int
+oce_ring_intr_disable(mac_intr_handle_t ring_handle)
+{
+ struct oce_rq *rx_ring = (struct oce_rq *)ring_handle;
+ struct oce_dev *dev;
+
+ dev = rx_ring->parent;
+ mutex_enter(&rx_ring->rx_lock);
+ oce_arm_cq(dev, rx_ring->cq->cq_id, 0, B_FALSE);
+ rx_ring->qmode = OCE_MODE_POLL;
+ mutex_exit(&rx_ring->rx_lock);
+ return (DDI_SUCCESS);
+}
+
+uint_t
+oce_ring_common_drain(struct oce_eq *eq)
+{
struct oce_eqe *eqe;
uint16_t num_eqe = 0;
uint16_t cq_id;
struct oce_cq *cq;
struct oce_dev *dev;
- _NOTE(ARGUNUSED(arg2));
-
- eq = (struct oce_eq *)(void *)(arg1);
-
dev = eq->parent;
eqe = RING_GET_CONSUMER_ITEM_VA(eq->ring, struct oce_eqe);
- while (eqe->u0.dw0) {
-
+ if (eqe->u0.dw0) {
eqe->u0.dw0 = LE_32(eqe->u0.dw0);
- /* if not CQ then continue else flag an error */
- if (EQ_MAJOR_CODE_COMPLETION != eqe->u0.s.major_code) {
- oce_log(dev, CE_WARN, MOD_ISR,
- "NOT a CQ event. 0x%x",
- eqe->u0.s.major_code);
- }
-
/* get the cq from the eqe */
cq_id = eqe->u0.s.resource_id % OCE_MAX_CQ;
cq = dev->cq[cq_id];
- /* Call the completion handler */
- (void) cq->cq_handler(cq->cb_arg);
-
/* clear valid bit and progress eqe */
eqe->u0.dw0 = 0;
RING_GET(eq->ring, 1);
- eqe = RING_GET_CONSUMER_ITEM_VA(eq->ring, struct oce_eqe);
num_eqe++;
- } /* for all EQEs */
+ if (cq) {
+ /* Call the completion handler */
+ (void) cq->cq_handler(cq->cb_arg, 0, 0);
+ }
+ }
+
/* ring the eq doorbell, signify that it's done processing */
oce_arm_eq(dev, eq->eq_id, num_eqe, B_TRUE, B_TRUE);
- if (num_eqe > 0) {
+
+ return (num_eqe);
+
+} /* oce_ring_common_drain */
+
+
+/* MSI/INTX handler: common vector for TX/RX/MQ */
+uint_t
+oce_isr(caddr_t arg1, caddr_t arg2)
+{
+ struct oce_eq *eq;
+ struct oce_dev *dev;
+ uint16_t num_eqe = 0;
+
+ _NOTE(ARGUNUSED(arg2));
+
+ eq = (struct oce_eq *)(void *)(arg1);
+ dev = eq->parent;
+
+ if ((dev == NULL) ||
+ !(dev->state & STATE_INTR_ENABLED)) {
+ oce_log(dev, CE_WARN, MOD_CONFIG, "%s",
+ "Dummy interrupt received");
return (DDI_INTR_CLAIMED);
+ }
+
+ mutex_enter(&eq->lock);
+ if (eq->qstate != QSTARTED) {
+ mutex_exit(&eq->lock);
+ oce_log(dev, CE_WARN, MOD_CONFIG, "%s",
+ "oce_isr EQ Not started");
+ return (DDI_INTR_CLAIMED);
+ }
+ num_eqe = oce_ring_common_drain(eq);
+ mutex_exit(&eq->lock);
+ if (num_eqe) {
+ return (DDI_INTR_CLAIMED);
} else {
return (DDI_INTR_UNCLAIMED);
}
-} /* oce_msix_handler */
+}
+
+/*
+ * IRM callback routine
+ */
+int
+oce_cbfunc(dev_info_t *dip, ddi_cb_action_t cbaction, void *cbarg,
+ void *arg1, void *arg2)
+{
+ struct oce_dev *dev = (struct oce_dev *)arg1;
+ int count = (int)(uintptr_t)cbarg, ret = DDI_ENOTSUP;
+
+ _NOTE(ARGUNUSED(dip));
+ _NOTE(ARGUNUSED(arg2));
+
+ switch (cbaction) {
+ case DDI_CB_INTR_ADD:
+ case DDI_CB_INTR_REMOVE:
+
+ oce_log(dev, CE_NOTE, MOD_CONFIG,
+ "IRM cbaction %d count %d vectors %d max_vectors %d",
+ cbaction, count, dev->num_vectors, dev->max_vectors);
+ ret = oce_adjust_intrs(dev, cbaction, count);
+ if (ret != DDI_SUCCESS) {
+ oce_log(dev, CE_NOTE, MOD_CONFIG, "%s",
+ "IRM: Failed to adjust interrupts");
+ return (ret);
+ }
+ break;
+
+ default:
+ return (ret);
+ }
+ return (ret);
+}
+
+
+static int
+oce_adjust_intrs(struct oce_dev *dev, ddi_cb_action_t action, int count)
+{
+ int i, nallocd, ret;
+
+ if (count == 0)
+ return (DDI_SUCCESS);
+
+ if ((action == DDI_CB_INTR_ADD &&
+ dev->num_vectors + count > dev->max_vectors) ||
+ (action == DDI_CB_INTR_REMOVE &&
+ dev->num_vectors - count < OCE_MIN_VECTORS)) {
+ return (DDI_FAILURE);
+ }
+
+ if (!(dev->state & STATE_MAC_STARTED)) {
+ return (DDI_FAILURE);
+ }
+
+ mutex_enter(&dev->dev_lock);
+ dev->state |= STATE_INTR_ADJUST;
+ dev->suspended = B_TRUE;
+
+ /* stop the groups */
+ for (i = 0; i < dev->num_rx_groups; i++) {
+ mutex_enter(&dev->rx_group[i].grp_lock);
+ oce_suspend_group_rings(&dev->rx_group[i]);
+ oce_stop_group(&dev->rx_group[i], B_FALSE);
+ mutex_exit(&dev->rx_group[i].grp_lock);
+ }
+
+ oce_stop(dev);
+ oce_remove_handler(dev);
+ if (action == DDI_CB_INTR_ADD) {
+ /* allocate additional vectors */
+ ret = ddi_intr_alloc(dev->dip, dev->htable, DDI_INTR_TYPE_MSIX,
+ dev->num_vectors, count, &nallocd, DDI_INTR_ALLOC_NORMAL);
+
+ if (ret != DDI_SUCCESS) {
+ goto irm_fail;
+ }
+
+ /* update actual count of available interrupts */
+ dev->num_vectors += nallocd;
+ oce_log(dev, CE_NOTE, MOD_CONFIG,
+ "IRM: INTR_ADD - count=0x%x allocated=0x%x vectors=0x%x",
+ count, nallocd, dev->num_vectors);
+ } else {
+ /* free interrupt vectors */
+ for (i = dev->num_vectors - count;
+ i < dev->num_vectors; i++) {
+ ret = ddi_intr_free(dev->htable[i]);
+ if (ret != DDI_SUCCESS) {
+ oce_log(dev, CE_WARN, MOD_CONFIG,
+ "IRM: can't free vectors ret 0x%x", ret);
+ goto irm_fail;
+ }
+ dev->htable[i] = NULL;
+ }
+
+ /* update actual count of available interrupts */
+ dev->num_vectors -= count;
+ oce_log(dev, CE_NOTE, MOD_CONFIG,
+ "IRM: INTR_REMOVE - count = 0x%x vectors = 0x%x",
+ count, dev->num_vectors);
+ }
+
+ if (oce_setup_handlers(dev) != DDI_SUCCESS) {
+ oce_log(dev, CE_WARN, MOD_CONFIG, "%s",
+ "Failed to Setup handlers during IRM");
+ goto irm_fail;
+ }
+
+ /* re-start the device instance */
+ if (oce_start(dev) != DDI_SUCCESS) {
+ goto irm_fail;
+ }
+
+ ret = ddi_intr_get_pri(dev->htable[0], &dev->intr_pri);
+
+ if (ret != DDI_SUCCESS) {
+ goto irm_fail;
+ }
+
+ (void) ddi_intr_get_cap(dev->htable[0], &dev->intr_cap);
+
+ /* re-start the groups */
+ for (i = 0; i < dev->num_rx_groups; i++) {
+ mutex_enter(&dev->rx_group[i].grp_lock);
+ ret = oce_start_group(&dev->rx_group[i], B_FALSE);
+ if (ret == DDI_SUCCESS) {
+ ret = oce_resume_group_rings(&dev->rx_group[i]);
+ }
+ mutex_exit(&dev->rx_group[i].grp_lock);
+ if (ret != DDI_SUCCESS) {
+ goto irm_fail;
+ }
+ }
+ dev->state &= ~STATE_INTR_ADJUST;
+ dev->suspended = B_FALSE;
+ mutex_exit(&dev->dev_lock);
+
+ /* Wakeup all Tx rings */
+ for (i = 0; i < dev->tx_rings; i++) {
+ mac_tx_ring_update(dev->mac_handle,
+ dev->default_tx_rings[i].tx->handle);
+ }
+
+ return (DDI_SUCCESS);
+
+irm_fail:
+ ddi_fm_service_impact(dev->dip, DDI_SERVICE_LOST);
+ mutex_exit(&dev->dev_lock);
+ return (DDI_FAILURE);
+}