Print this page
NEX-1890 update oce from source provided by Emulex
@@ -17,31 +17,25 @@
* 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 containing the implementation of the driver entry points
* and related helper functions
*/
#include <oce_impl.h>
#include <oce_ioctl.h>
-/* array of properties supported by this driver */
-char *oce_priv_props[] = {
- "_tx_ring_size",
- "_tx_bcopy_limit",
- "_rx_ring_size",
- "_rx_bcopy_limit",
- NULL
-};
-
-extern int pow10[];
-
/* ---[ static function declarations ]----------------------------------- */
static int oce_set_priv_prop(struct oce_dev *dev, const char *name,
uint_t size, const void *val);
static int oce_get_priv_prop(struct oce_dev *dev, const char *name,
@@ -50,11 +44,11 @@
/* ---[ GLD entry points ]----------------------------------------------- */
int
oce_m_start(void *arg)
{
struct oce_dev *dev = arg;
- int ret;
+ int i;
mutex_enter(&dev->dev_lock);
if (dev->state & STATE_MAC_STARTED) {
mutex_exit(&dev->dev_lock);
@@ -63,100 +57,204 @@
if (dev->suspended) {
mutex_exit(&dev->dev_lock);
return (EIO);
}
- ret = oce_start(dev);
- if (ret != DDI_SUCCESS) {
+
+ /* allocate Tx buffers */
+ if (oce_init_tx(dev) != DDI_SUCCESS) {
mutex_exit(&dev->dev_lock);
- return (EIO);
+ oce_log(dev, CE_WARN, MOD_CONFIG, "%s",
+ "Failed to init rings");
+ return (DDI_FAILURE);
}
- dev->state |= STATE_MAC_STARTED;
+ if (oce_start(dev) != DDI_SUCCESS) {
+ oce_fini_tx(dev);
mutex_exit(&dev->dev_lock);
+ return (EIO);
+ }
+ dev->state |= STATE_MAC_STARTED;
+ /* initialise the group locks */
+ for (i = 0; i < dev->num_rx_groups; i++) {
+ mutex_init(&dev->rx_group[i].grp_lock, NULL, MUTEX_DRIVER,
+ DDI_INTR_PRI(dev->intr_pri));
+ }
+ mutex_exit(&dev->dev_lock);
+ oce_enable_wd_timer(dev);
return (DDI_SUCCESS);
}
+void
+oce_start_eqs(struct oce_dev *dev)
+{
+ int qidx = 0;
+
+ for (qidx = 0; qidx < dev->neqs; qidx++) {
+ mutex_enter(&dev->eq[qidx].lock);
+ oce_arm_eq(dev, dev->eq[qidx].eq_id, 0, B_TRUE, B_FALSE);
+ dev->eq[qidx].qstate = QSTARTED;
+ mutex_exit(&dev->eq[qidx].lock);
+ }
+}
+
+void
+oce_stop_eqs(struct oce_dev *dev)
+{
+ int qidx = 0;
+
+ for (qidx = 0; qidx < dev->neqs; qidx++) {
+ mutex_enter(&dev->eq[qidx].lock);
+ oce_arm_eq(dev, dev->eq[qidx].eq_id, 0, B_FALSE, B_FALSE);
+ dev->eq[qidx].qstate = QSTOPPED;
+ mutex_exit(&dev->eq[qidx].lock);
+ }
+}
int
oce_start(struct oce_dev *dev)
{
int qidx = 0;
- struct link_status link = {0};
- /* get link status */
- (void) oce_get_link_status(dev, &link);
+ /* disable the interrupts */
+ if (!LANCER_CHIP(dev))
+ oce_chip_di(dev);
- dev->link_status = (link.logical_link_status == NTWK_LOGICAL_LINK_UP) ?
- LINK_STATE_UP : LINK_STATE_DOWN;
+ /* set default flow control */
+ (void) oce_set_flow_control(dev, dev->flow_control, MBX_BOOTSTRAP);
+ (void) oce_set_promiscuous(dev, dev->promisc, MBX_BOOTSTRAP);
- dev->link_speed = link.qos_link_speed ? link.qos_link_speed * 10 :
- pow10[link.mac_speed];
+ if (oce_ei(dev) != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
- mac_link_update(dev->mac_handle, dev->link_status);
+ if (oce_create_queues(dev) != DDI_SUCCESS) {
+ goto cleanup_handler;
+ }
- for (qidx = 0; qidx < dev->nwqs; qidx++) {
- (void) oce_start_wq(dev->wq[qidx]);
+ for (qidx = 0; qidx < dev->tx_rings; qidx++) {
+ mac_ring_intr_set(dev->default_tx_rings[qidx].tx->handle,
+ dev->htable[dev->default_tx_rings[qidx].tx->cq->eq->idx]);
+ (void) oce_start_wq(dev->default_tx_rings[qidx].tx);
}
- for (qidx = 0; qidx < dev->nrqs; qidx++) {
- (void) oce_start_rq(dev->rq[qidx]);
+
+ if (oce_create_mcc_queue(dev) != DDI_SUCCESS) {
+ goto delete_queues;
}
(void) oce_start_mq(dev->mq);
- /* enable interrupts */
- oce_ei(dev);
+
+ dev->state |= STATE_INTR_ENABLED;
+
+ if (!LANCER_CHIP(dev))
+ oce_chip_ei(dev);
+
/* arm the eqs */
- for (qidx = 0; qidx < dev->neqs; qidx++) {
- oce_arm_eq(dev, dev->eq[qidx]->eq_id, 0, B_TRUE, B_FALSE);
+ oce_start_eqs(dev);
+
+ /* get link status */
+ if (oce_get_link_status(dev, &dev->link_status, &dev->link_speed,
+ (uint8_t *)&dev->link_duplex, 1, MBX_ASYNC_MQ) != DDI_SUCCESS) {
+ (void) oce_get_link_status(dev, &dev->link_status,
+ &dev->link_speed, (uint8_t *)&dev->link_duplex,
+ 0, MBX_ASYNC_MQ);
}
- /* TODO update state */
+ oce_log(dev, CE_NOTE, MOD_CONFIG, "link speed %d "
+ "link status %d", dev->link_speed, dev->link_status);
+
+ mac_link_update(dev->mac_handle, dev->link_status);
return (DDI_SUCCESS);
+
+delete_queues:
+ oce_delete_queues(dev);
+cleanup_handler:
+ (void) oce_di(dev);
+ return (DDI_FAILURE);
} /* oce_start */
void
oce_m_stop(void *arg)
{
struct oce_dev *dev = arg;
+ int i;
- /* disable interrupts */
-
mutex_enter(&dev->dev_lock);
if (dev->suspended) {
mutex_exit(&dev->dev_lock);
return;
}
- dev->state |= STATE_MAC_STOPPING;
+
+ dev->state &= ~STATE_MAC_STARTED;
oce_stop(dev);
- dev->state &= ~(STATE_MAC_STOPPING | STATE_MAC_STARTED);
+
+ /* free Tx buffers */
+ oce_fini_tx(dev);
+
+ for (i = 0; i < dev->rx_rings; i++) {
+ while (dev->rq[i].pending > 0) {
+ oce_log(dev, CE_NOTE, MOD_CONFIG,
+ "%d pending buffers on rq %p\n",
+ dev->rq[i].pending, (void *)&dev->rq[i]);
+ drv_usecwait(10 * 1000);
+ }
+ }
+
+ /* destroy group locks */
+ for (i = 0; i < dev->num_rx_groups; i++) {
+ mutex_destroy(&dev->rx_group[i].grp_lock);
+ }
+
mutex_exit(&dev->dev_lock);
+ oce_disable_wd_timer(dev);
}
+
+
/* called with Tx/Rx comp locks held */
void
oce_stop(struct oce_dev *dev)
{
int qidx;
+
+ dev->state |= STATE_MAC_STOPPING;
+
/* disable interrupts */
- oce_di(dev);
+ (void) oce_di(dev);
+ oce_stop_eqs(dev);
+ dev->state &= (~STATE_INTR_ENABLED);
+
for (qidx = 0; qidx < dev->nwqs; qidx++) {
- mutex_enter(&dev->wq[qidx]->tx_lock);
+ mac_ring_intr_set(dev->default_tx_rings[qidx].tx->handle, NULL);
+ mutex_enter(&dev->wq[qidx].tx_lock);
}
mutex_enter(&dev->mq->lock);
- /* complete the pending Tx */
- for (qidx = 0; qidx < dev->nwqs; qidx++)
- oce_clean_wq(dev->wq[qidx]);
+
+ for (qidx = 0; qidx < dev->tx_rings; qidx++) {
+ /* stop and flush the Tx */
+ (void) oce_clean_wq(dev->default_tx_rings[qidx].tx);
+ }
+
+ /* Free the pending commands */
+ oce_clean_mq(dev->mq);
+
/* Release all the locks */
mutex_exit(&dev->mq->lock);
for (qidx = 0; qidx < dev->nwqs; qidx++)
- mutex_exit(&dev->wq[qidx]->tx_lock);
+ mutex_exit(&dev->wq[qidx].tx_lock);
+
if (dev->link_status == LINK_STATE_UP) {
dev->link_status = LINK_STATE_UNKNOWN;
mac_link_update(dev->mac_handle, dev->link_status);
}
+ oce_delete_mcc_queue(dev);
+ oce_delete_queues(dev);
+
+ dev->state &= ~STATE_MAC_STOPPING;
} /* oce_stop */
+
int
oce_m_multicast(void *arg, boolean_t add, const uint8_t *mca)
{
struct oce_dev *dev = (struct oce_dev *)arg;
struct ether_addr *mca_drv_list;
@@ -163,14 +261,10 @@
struct ether_addr mca_hw_list[OCE_MAX_MCA];
uint16_t new_mcnt = dev->num_mca;
int ret;
int i;
- /* check the address */
- if ((mca[0] & 0x1) == 0) {
- return (EINVAL);
- }
/* Allocate the local array for holding the addresses temporarily */
bzero(&mca_hw_list, sizeof (&mca_hw_list));
mca_drv_list = &dev->multi_cast[0];
DEV_LOCK(dev);
@@ -192,29 +286,29 @@
/* copy only if it does not match */
if (bcmp((mca_drv_list + i), mca, ETHERADDRL)) {
bcopy(mca_drv_list + i, hwlistp,
ETHERADDRL);
hwlistp++;
- } else {
- new_mcnt--;
}
}
+ /* Decrement the count */
+ new_mcnt--;
}
if (dev->suspended) {
goto finish;
}
if (new_mcnt > OCE_MAX_MCA) {
ret = oce_set_multicast_table(dev, dev->if_id, &mca_hw_list[0],
- OCE_MAX_MCA, B_TRUE);
+ OCE_MAX_MCA, B_TRUE, MBX_BOOTSTRAP);
} else {
ret = oce_set_multicast_table(dev, dev->if_id,
- &mca_hw_list[0], new_mcnt, B_FALSE);
+ &mca_hw_list[0], new_mcnt, B_FALSE, MBX_BOOTSTRAP);
}
if (ret != 0) {
oce_log(dev, CE_WARN, MOD_CONFIG,
- "mcast %s fails", add ? "ADD" : "DEL");
+ "mcast %s failed 0x%x", add ? "ADD" : "DEL", ret);
DEV_UNLOCK(dev);
return (EIO);
}
/*
* Copy the local structure to dev structure
@@ -233,88 +327,11 @@
mca[0], mca[1], mca[2], mca[3], mca[4], mca[5],
dev->num_mca);
return (0);
} /* oce_m_multicast */
-int
-oce_m_unicast(void *arg, const uint8_t *uca)
-{
- struct oce_dev *dev = arg;
- int ret;
- DEV_LOCK(dev);
- if (dev->suspended) {
- bcopy(uca, dev->unicast_addr, ETHERADDRL);
- dev->num_smac = 0;
- DEV_UNLOCK(dev);
- return (DDI_SUCCESS);
- }
-
- /* Delete previous one and add new one */
- ret = oce_del_mac(dev, dev->if_id, &dev->pmac_id);
- if (ret != DDI_SUCCESS) {
- DEV_UNLOCK(dev);
- return (EIO);
- }
- dev->num_smac = 0;
- bzero(dev->unicast_addr, ETHERADDRL);
-
- /* Set the New MAC addr earlier is no longer valid */
- ret = oce_add_mac(dev, dev->if_id, uca, &dev->pmac_id);
- if (ret != DDI_SUCCESS) {
- DEV_UNLOCK(dev);
- return (EIO);
- }
- bcopy(uca, dev->unicast_addr, ETHERADDRL);
- dev->num_smac = 1;
- DEV_UNLOCK(dev);
- return (ret);
-} /* oce_m_unicast */
-
-/*
- * Hashing policy for load balancing over the set of TX rings
- * available to the driver.
- */
-mblk_t *
-oce_m_send(void *arg, mblk_t *mp)
-{
- struct oce_dev *dev = arg;
- mblk_t *nxt_pkt;
- mblk_t *rmp = NULL;
- struct oce_wq *wq;
-
- DEV_LOCK(dev);
- if (dev->suspended || !(dev->state & STATE_MAC_STARTED)) {
- DEV_UNLOCK(dev);
- freemsg(mp);
- return (NULL);
- }
- DEV_UNLOCK(dev);
- /*
- * Hash to pick a wq
- */
- wq = oce_get_wq(dev, mp);
-
- while (mp != NULL) {
- /* Save the Pointer since mp will be freed in case of copy */
- nxt_pkt = mp->b_next;
- mp->b_next = NULL;
- /* Hardcode wq since we have only one */
- rmp = oce_send_packet(wq, mp);
- if (rmp != NULL) {
- /* reschedule Tx */
- wq->resched = B_TRUE;
- oce_arm_cq(dev, wq->cq->cq_id, 0, B_TRUE);
- /* restore the chain */
- rmp->b_next = nxt_pkt;
- break;
- }
- mp = nxt_pkt;
- }
- return (rmp);
-} /* oce_send */
-
boolean_t
oce_m_getcap(void *arg, mac_capab_t cap, void *data)
{
struct oce_dev *dev = arg;
boolean_t ret = B_TRUE;
@@ -335,10 +352,15 @@
} else {
ret = B_FALSE;
}
break;
}
+ case MAC_CAPAB_RINGS:
+
+ ret = oce_fill_rings_capab(dev, (mac_capab_rings_t *)data);
+ break;
+
default:
ret = B_FALSE;
break;
}
return (ret);
@@ -366,10 +388,15 @@
if (mtu != OCE_MIN_MTU && mtu != OCE_MAX_MTU) {
ret = EINVAL;
break;
}
+ if (dev->state & STATE_MAC_STARTED) {
+ ret = EBUSY;
+ break;
+ }
+
ret = mac_maxsdu_update(dev->mac_handle, mtu);
if (0 == ret) {
dev->mtu = mtu;
break;
}
@@ -412,11 +439,11 @@
if (dev->suspended) {
dev->flow_control = fc;
break;
}
/* call to set flow control */
- ret = oce_set_flow_control(dev, fc);
+ ret = oce_set_flow_control(dev, fc, MBX_ASYNC_MQ);
/* store the new fc setting on success */
if (ret == 0) {
dev->flow_control = fc;
}
break;
@@ -458,26 +485,13 @@
*mode = LINK_DUPLEX_UNKNOWN;
break;
}
case MAC_PROP_SPEED: {
- uint64_t *speed = (uint64_t *)val;
- struct link_status link = {0};
-
- ASSERT(size >= sizeof (uint64_t));
- *speed = 0;
-
- if (dev->state & STATE_MAC_STARTED) {
- if (dev->link_speed < 0) {
- (void) oce_get_link_status(dev, &link);
- dev->link_speed = link.qos_link_speed ?
- link.qos_link_speed * 10 :
- pow10[link.mac_speed];
- }
-
- *speed = dev->link_speed * 1000000ull;
- }
+ uint64_t speed;
+ speed = dev->link_speed * 1000000ull;
+ bcopy(&speed, val, sizeof (speed));
break;
}
case MAC_PROP_FLOWCTRL: {
link_flowctrl_t *fc = (link_flowctrl_t *)val;
@@ -543,22 +557,45 @@
break;
case MAC_PROP_PRIVATE: {
char valstr[64];
int value;
+ uint_t perm = MAC_PROP_PERM_READ;
- if (strcmp(name, "_tx_ring_size") == 0) {
+ bzero(valstr, sizeof (valstr));
+ if (strcmp(name, "_tx_rings") == 0) {
+ value = OCE_DEFAULT_WQS;
+ } else if (strcmp(name, "_tx_ring_size") == 0) {
value = OCE_DEFAULT_TX_RING_SIZE;
+ perm = MAC_PROP_PERM_RW;
+ } else if (strcmp(name, "_tx_bcopy_limit") == 0) {
+ value = OCE_DEFAULT_TX_BCOPY_LIMIT;
+ perm = MAC_PROP_PERM_RW;
+ } else if (strcmp(name, "_tx_reclaim_threshold") == 0) {
+ value = OCE_DEFAULT_TX_RECLAIM_THRESHOLD;
+ perm = MAC_PROP_PERM_RW;
+ } else if (strcmp(name, "_rx_rings") == 0) {
+ value = OCE_DEFAULT_RQS;
+ } else if (strcmp(name, "_rx_rings_per_group") == 0) {
+ value = OCE_DEF_RING_PER_GROUP;
} else if (strcmp(name, "_rx_ring_size") == 0) {
value = OCE_DEFAULT_RX_RING_SIZE;
- } else {
+ } else if (strcmp(name, "_rx_bcopy_limit") == 0) {
+ value = OCE_DEFAULT_RX_BCOPY_LIMIT;
+ perm = MAC_PROP_PERM_RW;
+ } else if (strcmp(name, "_rx_pkts_per_intr") == 0) {
+ value = OCE_DEFAULT_RX_PKTS_PER_INTR;
+ perm = MAC_PROP_PERM_RW;
+ } else if (strcmp(name, "_log_level") == 0) {
+ value = OCE_DEFAULT_LOG_SETTINGS;
+ perm = MAC_PROP_PERM_RW;
+ } else
return;
- }
(void) snprintf(valstr, sizeof (valstr), "%d", value);
mac_prop_info_set_default_str(prh, valstr);
- mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
+ mac_prop_info_set_perm(prh, perm);
break;
}
}
} /* oce_m_propinfo */
@@ -587,11 +624,11 @@
DEV_UNLOCK(dev);
switch (cmd) {
case OCE_ISSUE_MBOX: {
- ret = oce_issue_mbox(dev, wq, mp, &payload_length);
+ ret = oce_issue_mbox_passthru(dev, wq, mp, &payload_length);
miocack(wq, mp, payload_length, ret);
break;
}
case OCE_QUERY_DRIVER_DATA: {
struct oce_driver_query *drv_query =
@@ -657,13 +694,27 @@
dev->promisc = enable;
DEV_UNLOCK(dev);
return (ret);
}
- ret = oce_set_promiscuous(dev, enable);
- if (ret == DDI_SUCCESS)
+ ret = oce_set_promiscuous(dev, enable, MBX_ASYNC_MQ);
+ if (ret == DDI_SUCCESS) {
dev->promisc = enable;
+ if (!(enable)) {
+ struct ether_addr *mca_drv_list;
+ mca_drv_list = &dev->multi_cast[0];
+ if (dev->num_mca > OCE_MAX_MCA) {
+ ret = oce_set_multicast_table(dev, dev->if_id,
+ &mca_drv_list[0], OCE_MAX_MCA, B_TRUE,
+ MBX_ASYNC_MQ);
+ } else {
+ ret = oce_set_multicast_table(dev, dev->if_id,
+ &mca_drv_list[0], dev->num_mca, B_FALSE,
+ MBX_ASYNC_MQ);
+ }
+ }
+ }
DEV_UNLOCK(dev);
return (ret);
} /* oce_m_promiscuous */
/*
@@ -679,40 +730,61 @@
*/
static int
oce_set_priv_prop(struct oce_dev *dev, const char *name,
uint_t size, const void *val)
{
- int ret = ENOTSUP;
+ int ret = EINVAL;
long result;
_NOTE(ARGUNUSED(size));
if (NULL == val) {
- ret = EINVAL;
- return (ret);
+ return (EINVAL);
}
-
- if (strcmp(name, "_tx_bcopy_limit") == 0) {
(void) ddi_strtol(val, (char **)NULL, 0, &result);
- if (result <= OCE_WQ_BUF_SIZE) {
+ if (strcmp(name, "_tx_ring_size") == 0) {
+ if (result <= SIZE_2K) {
+ if (dev->tx_ring_size != result) {
+ dev->tx_ring_size = (uint32_t)result;
+ }
+ ret = 0;
+ }
+ } else if (strcmp(name, "_tx_bcopy_limit") == 0) {
+ if (result <= SIZE_2K) {
if (result != dev->tx_bcopy_limit)
dev->tx_bcopy_limit = (uint32_t)result;
ret = 0;
- } else {
- ret = EINVAL;
}
+ } else if (strcmp(name, "_tx_reclaim_threshold") == 0) {
+ if (result <= dev->tx_ring_size) {
+ if (dev->tx_reclaim_threshold != result) {
+ dev->tx_reclaim_threshold = (uint32_t)result;
}
- if (strcmp(name, "_rx_bcopy_limit") == 0) {
- (void) ddi_strtol(val, (char **)NULL, 0, &result);
- if (result <= OCE_RQ_BUF_SIZE) {
- if (result != dev->rx_bcopy_limit)
+ ret = 0;
+ }
+ } else if (strcmp(name, "_rx_bcopy_limit") == 0) {
+ if (result <= dev->mtu) {
+ if (dev->rx_bcopy_limit != result) {
dev->rx_bcopy_limit = (uint32_t)result;
+ }
ret = 0;
- } else {
- ret = EINVAL;
}
+ } else if (strcmp(name, "_rx_pkts_per_intr") == 0) {
+ if (result <= dev->rx_ring_size) {
+ if (dev->rx_pkt_per_intr != result) {
+ dev->rx_pkt_per_intr = (uint32_t)result;
}
+ ret = 0;
+ }
+ } else if (strcmp(name, "_log_level") == 0) {
+ if (result <= OCE_MAX_LOG_SETTINGS) {
+ /* derive from the loglevel */
+ dev->severity = (uint16_t)(result & 0xffff);
+ dev->mod_mask = (uint16_t)(result >> 16);
+ }
+ ret = 0;
+ }
return (ret);
} /* oce_set_priv_prop */
/*
@@ -729,18 +801,30 @@
oce_get_priv_prop(struct oce_dev *dev, const char *name,
uint_t size, void *val)
{
int value;
- if (strcmp(name, "_tx_ring_size") == 0) {
+ if (strcmp(name, "_tx_rings") == 0) {
+ value = dev->tx_rings;
+ } else if (strcmp(name, "_tx_ring_size") == 0) {
value = dev->tx_ring_size;
} else if (strcmp(name, "_tx_bcopy_limit") == 0) {
value = dev->tx_bcopy_limit;
+ } else if (strcmp(name, "_tx_reclaim_threshold") == 0) {
+ value = dev->tx_reclaim_threshold;
+ } else if (strcmp(name, "_rx_rings") == 0) {
+ value = dev->rx_rings;
+ } else if (strcmp(name, "_rx_rings_per_group") == 0) {
+ value = dev->rx_rings_per_group;
} else if (strcmp(name, "_rx_ring_size") == 0) {
value = dev->rx_ring_size;
} else if (strcmp(name, "_rx_bcopy_limit") == 0) {
value = dev->rx_bcopy_limit;
+ } else if (strcmp(name, "_rx_pkts_per_intr") == 0) {
+ value = dev->rx_pkt_per_intr;
+ } else if (strcmp(name, "_log_level") == 0) {
+ value = (dev->mod_mask << 16UL) | dev->severity;
} else {
return (ENOTSUP);
}
(void) snprintf(val, size, "%d", value);