Rework interrupt bringup and teardown.

Calculate the exact number of vectors we'll use before calling
pci_alloc_msix.  Don't grab nine all the time.

Call cxgb_setup_interrupts once per T3, not once per port.  Ditto
for cxgb_teardown_interrupts.

Don't leak resources when interrupt setup fails in the middle.

Obtained from:	Navdeep Parhar
MFC after:	10 days
This commit is contained in:
gnn 2009-05-27 20:13:36 +00:00
parent 979cd40951
commit 1280b164b6

View File

@ -82,8 +82,8 @@ __FBSDID("$FreeBSD$");
#include <sys/priv.h>
#endif
static int cxgb_setup_msix(adapter_t *, int);
static void cxgb_teardown_msix(adapter_t *);
static int cxgb_setup_interrupts(adapter_t *);
static void cxgb_teardown_interrupts(adapter_t *);
static void cxgb_init(void *);
static void cxgb_init_locked(struct port_info *);
static void cxgb_stop_locked(struct port_info *);
@ -175,8 +175,6 @@ static struct cdevsw cxgb_cdevsw = {
static devclass_t cxgb_port_devclass;
DRIVER_MODULE(cxgb, cxgbc, cxgb_port_driver, cxgb_port_devclass, 0, 0);
#define SGE_MSIX_COUNT (SGE_QSETS + 1)
/*
* The driver uses the best interrupt scheme available on a platform in the
* order MSI-X, MSI, legacy pin interrupts. This parameter determines which
@ -517,46 +515,52 @@ cxgb_controller_attach(device_t dev)
(sc->msix_regs_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
&sc->msix_regs_rid, RF_ACTIVE)) != NULL) {
msi_needed = sc->msi_count = SGE_MSIX_COUNT;
if (multiq)
port_qsets = min(SGE_QSETS/sc->params.nports, mp_ncpus);
msi_needed = sc->msi_count = sc->params.nports * port_qsets + 1;
if (((error = pci_alloc_msix(dev, &sc->msi_count)) != 0) ||
(sc->msi_count != msi_needed)) {
device_printf(dev, "msix allocation failed - msi_count = %d"
" msi_needed=%d will try msi err=%d\n", sc->msi_count,
msi_needed, error);
if (pci_msix_count(dev) == 0 ||
(error = pci_alloc_msix(dev, &sc->msi_count)) != 0 ||
sc->msi_count != msi_needed) {
device_printf(dev, "alloc msix failed - "
"msi_count=%d, msi_needed=%d, err=%d; "
"will try MSI\n", sc->msi_count,
msi_needed, error);
sc->msi_count = 0;
port_qsets = 1;
pci_release_msi(dev);
bus_release_resource(dev, SYS_RES_MEMORY,
sc->msix_regs_rid, sc->msix_regs_res);
sc->msix_regs_res = NULL;
} else {
sc->flags |= USING_MSIX;
sc->cxgb_intr = t3_intr_msix;
sc->cxgb_intr = cxgb_async_intr;
device_printf(dev,
"using MSI-X interrupts (%u vectors)\n",
sc->msi_count);
}
}
if ((msi_allowed >= 1) && (sc->msi_count == 0)) {
sc->msi_count = 1;
if (pci_alloc_msi(dev, &sc->msi_count)) {
device_printf(dev, "alloc msi failed - will try INTx\n");
if ((error = pci_alloc_msi(dev, &sc->msi_count)) != 0) {
device_printf(dev, "alloc msi failed - "
"err=%d; will try INTx\n", error);
sc->msi_count = 0;
port_qsets = 1;
pci_release_msi(dev);
} else {
sc->flags |= USING_MSI;
sc->irq_rid = 1;
sc->cxgb_intr = t3_intr_msi;
device_printf(dev, "using MSI interrupts\n");
}
}
#endif
if (sc->msi_count == 0) {
device_printf(dev, "using line interrupts\n");
sc->irq_rid = 0;
sc->cxgb_intr = t3b_intr;
}
if ((sc->flags & USING_MSIX) && multiq)
port_qsets = min((SGE_QSETS/(sc)->params.nports), mp_ncpus);
/* Create a private taskqueue thread for handling driver events */
#ifdef TASKQUEUE_CURRENT
sc->tq = taskqueue_create("cxgb_taskq", M_NOWAIT,
@ -695,7 +699,7 @@ cxgb_controller_detach(device_t dev)
* 3. Detaching all of the port devices created during the
* cxgb_controller_attach() routine.
* 4. Removing the device children created via cxgb_controller_attach().
* 5. Releaseing PCI resources associated with the device.
* 5. Releasing PCI resources associated with the device.
* 6. Turning off the offload support, iff it was turned on.
* 7. Destroying the mutexes created in cxgb_controller_attach().
*
@ -730,6 +734,8 @@ cxgb_free(struct adapter *sc)
device_printf(sc->dev, "failed to delete child port\n");
}
cxgb_teardown_interrupts(sc);
#ifdef MSI_SUPPORTED
if (sc->flags & (USING_MSI | USING_MSIX)) {
device_printf(sc->dev, "releasing msi message(s)\n");
@ -737,11 +743,12 @@ cxgb_free(struct adapter *sc)
} else {
device_printf(sc->dev, "no msi message to release\n");
}
#endif
if (sc->msix_regs_res != NULL) {
bus_release_resource(sc->dev, SYS_RES_MEMORY, sc->msix_regs_rid,
sc->msix_regs_res);
}
#endif
if (sc->tq != NULL) {
taskqueue_free(sc->tq);
@ -821,91 +828,116 @@ setup_sge_qsets(adapter_t *sc)
}
static void
cxgb_teardown_msix(adapter_t *sc)
cxgb_teardown_interrupts(adapter_t *sc)
{
int i, nqsets;
for (nqsets = i = 0; i < (sc)->params.nports; i++)
nqsets += sc->port[i].nqsets;
int i;
for (i = 0; i < nqsets; i++) {
if (sc->msix_intr_tag[i] != NULL) {
bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
sc->msix_intr_tag[i]);
sc->msix_intr_tag[i] = NULL;
}
if (sc->msix_irq_res[i] != NULL) {
bus_release_resource(sc->dev, SYS_RES_IRQ,
sc->msix_irq_rid[i], sc->msix_irq_res[i]);
sc->msix_irq_res[i] = NULL;
for (i = 0; i < SGE_QSETS; i++) {
if (sc->msix_intr_tag[i] == NULL) {
/* Should have been setup fully or not at all */
KASSERT(sc->msix_irq_res[i] == NULL &&
sc->msix_irq_rid[i] == 0,
("%s: half-done interrupt (%d).", __func__, i));
continue;
}
bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
sc->msix_intr_tag[i]);
bus_release_resource(sc->dev, SYS_RES_IRQ, sc->msix_irq_rid[i],
sc->msix_irq_res[i]);
sc->msix_irq_res[i] = sc->msix_intr_tag[i] = NULL;
sc->msix_irq_rid[i] = 0;
}
if (sc->intr_tag) {
KASSERT(sc->irq_res != NULL,
("%s: half-done interrupt.", __func__));
bus_teardown_intr(sc->dev, sc->irq_res, sc->intr_tag);
bus_release_resource(sc->dev, SYS_RES_IRQ, sc->irq_rid,
sc->irq_res);
sc->irq_res = sc->intr_tag = NULL;
sc->irq_rid = 0;
}
}
static int
cxgb_setup_msix(adapter_t *sc, int msix_count)
cxgb_setup_interrupts(adapter_t *sc)
{
int i, j, k, nqsets, rid;
struct resource *res;
void *tag;
int i, rid, err, intr_flag = sc->flags & (USING_MSI | USING_MSIX);
/* The first message indicates link changes and error conditions */
sc->irq_rid = 1;
if ((sc->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ,
&sc->irq_rid, RF_SHAREABLE | RF_ACTIVE)) == NULL) {
device_printf(sc->dev, "Cannot allocate msix interrupt\n");
return (EINVAL);
}
if (bus_setup_intr(sc->dev, sc->irq_res, INTR_MPSAFE|INTR_TYPE_NET,
sc->irq_rid = intr_flag ? 1 : 0;
sc->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &sc->irq_rid,
RF_SHAREABLE | RF_ACTIVE);
if (sc->irq_res == NULL) {
device_printf(sc->dev, "Cannot allocate interrupt (%x, %u)\n",
intr_flag, sc->irq_rid);
err = EINVAL;
sc->irq_rid = 0;
} else {
err = bus_setup_intr(sc->dev, sc->irq_res,
INTR_MPSAFE | INTR_TYPE_NET,
#ifdef INTR_FILTERS
NULL,
NULL,
#endif
cxgb_async_intr, sc, &sc->intr_tag)) {
device_printf(sc->dev, "Cannot set up interrupt\n");
return (EINVAL);
}
for (i = k = 0; i < (sc)->params.nports; i++) {
nqsets = sc->port[i].nqsets;
for (j = 0; j < nqsets; j++, k++) {
struct sge_qset *qs = &sc->sge.qs[k];
sc->cxgb_intr, sc, &sc->intr_tag);
rid = k + 2;
if (cxgb_debug)
printf("rid=%d ", rid);
if ((sc->msix_irq_res[k] = bus_alloc_resource_any(
sc->dev, SYS_RES_IRQ, &rid,
RF_SHAREABLE | RF_ACTIVE)) == NULL) {
device_printf(sc->dev, "Cannot allocate "
"interrupt for message %d\n", rid);
return (EINVAL);
}
sc->msix_irq_rid[k] = rid;
if (bus_setup_intr(sc->dev, sc->msix_irq_res[k],
INTR_MPSAFE|INTR_TYPE_NET,
#ifdef INTR_FILTERS
NULL,
#endif
t3_intr_msix, qs, &sc->msix_intr_tag[k])) {
device_printf(sc->dev, "Cannot set up "
"interrupt for message %d\n", rid);
return (EINVAL);
}
#if 0
#ifdef IFNET_MULTIQUEUE
if (multiq) {
int vector = rman_get_start(sc->msix_irq_res[k]);
if (bootverbose)
device_printf(sc->dev, "binding vector=%d to cpu=%d\n", vector, k % mp_ncpus);
intr_bind(vector, k % mp_ncpus);
}
#endif
#endif
if (err) {
device_printf(sc->dev,
"Cannot set up interrupt (%x, %u, %d)\n",
intr_flag, sc->irq_rid, err);
bus_release_resource(sc->dev, SYS_RES_IRQ, sc->irq_rid,
sc->irq_res);
sc->irq_res = sc->intr_tag = NULL;
sc->irq_rid = 0;
}
}
return (0);
/* That's all for INTx or MSI */
if (!(intr_flag & USING_MSIX) || err)
return (err);
for (i = 0; i < sc->msi_count - 1; i++) {
rid = i + 2;
res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &rid,
RF_SHAREABLE | RF_ACTIVE);
if (res == NULL) {
device_printf(sc->dev, "Cannot allocate interrupt "
"for message %d\n", rid);
err = EINVAL;
break;
}
err = bus_setup_intr(sc->dev, res, INTR_MPSAFE | INTR_TYPE_NET,
#ifdef INTR_FILTERS
NULL,
#endif
t3_intr_msix, &sc->sge.qs[i], &tag);
if (err) {
device_printf(sc->dev, "Cannot set up interrupt "
"for message %d (%d)\n", rid, err);
bus_release_resource(sc->dev, SYS_RES_IRQ, rid, res);
break;
}
sc->msix_irq_rid[i] = rid;
sc->msix_irq_res[i] = res;
sc->msix_intr_tag[i] = tag;
}
if (err)
cxgb_teardown_interrupts(sc);
return (err);
}
static int
cxgb_port_probe(device_t dev)
{
@ -1075,39 +1107,12 @@ cxgb_port_attach(device_t dev)
bcopy(IF_LLADDR(p->ifp), p->hw_addr, ETHER_ADDR_LEN);
t3_sge_init_port(p);
/* If it's MSI or INTx, allocate a single interrupt for everything */
if ((sc->flags & USING_MSIX) == 0) {
if ((sc->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ,
&sc->irq_rid, RF_SHAREABLE | RF_ACTIVE)) == NULL) {
device_printf(sc->dev, "Cannot allocate interrupt rid=%d\n",
sc->irq_rid);
err = EINVAL;
goto out;
}
device_printf(sc->dev, "allocated irq_res=%p\n", sc->irq_res);
if (bus_setup_intr(sc->dev, sc->irq_res, INTR_MPSAFE|INTR_TYPE_NET,
#ifdef INTR_FILTERS
NULL,
#endif
sc->cxgb_intr, sc, &sc->intr_tag)) {
device_printf(sc->dev, "Cannot set up interrupt\n");
err = EINVAL;
goto irq_err;
}
} else {
cxgb_setup_msix(sc, sc->msi_count);
}
#if defined(LINK_ATTACH)
cxgb_link_start(p);
t3_link_changed(sc, p->port_id);
#endif
out:
return (err);
irq_err:
CH_ERR(sc, "request_irq failed, err %d\n", err);
goto out;
}
/*
@ -1131,29 +1136,12 @@ cxgb_port_detach(device_t dev)
destroy_dev(p->port_cdev);
ether_ifdetach(p->ifp);
printf("waiting for callout to stop ...");
printf("done\n");
PORT_LOCK(p);
if (p->ifp->if_drv_flags & IFF_DRV_RUNNING)
cxgb_stop_locked(p);
PORT_UNLOCK(p);
if (sc->intr_tag != NULL) {
bus_teardown_intr(sc->dev, sc->irq_res, sc->intr_tag);
sc->intr_tag = NULL;
}
if (sc->irq_res != NULL) {
device_printf(sc->dev, "de-allocating interrupt irq_rid=%d irq_res=%p\n",
sc->irq_rid, sc->irq_res);
bus_release_resource(sc->dev, SYS_RES_IRQ, sc->irq_rid,
sc->irq_res);
sc->irq_res = NULL;
}
if (sc->flags & USING_MSIX)
cxgb_teardown_msix(sc);
callout_drain(&sc->cxgb_tick_ch);
callout_drain(&sc->sge_timer_ch);
@ -1740,12 +1728,17 @@ cxgb_up(struct adapter *sc)
goto out;
setup_rss(sc);
t3_intr_clear(sc);
err = cxgb_setup_interrupts(sc);
if (err)
goto out;
t3_add_configured_sysctls(sc);
sc->flags |= FULL_INIT_DONE;
}
t3_intr_clear(sc);
t3_sge_start(sc);
t3_intr_enable(sc);