Module Name: src
Committed By: jdolecek
Date: Sun Sep 18 21:19:39 UTC 2016
Modified Files:
src/sys/dev/ic: ld_nvme.c nvme.c nvmevar.h
src/sys/dev/pci: nvme_pci.c
Log Message:
fix several bugs, make nvme(4) MPSAFE by default and also bump default
number of ioq from 128 to 1024; tested with VirtualBox and QEMU
* remove NVME_INTMC/NVME_INTMS writes in hw intr handler as this is not MPSAFE,
fortunately they don't seem to be necessary; shaves two register writes
* need to use full mutex_enter() in nvme_q_complete(), to avoid small
race between one handler exiting the loop and another entering
* for MSI, handover the command result processing to softintr; unfortunately
can't easily do that for INTx interrupts as they require doorbell write
to deassert
* unlock/relock q->q_cq_mtx before calling ccb_done to avoid potential deadlocks
* make sure to destroy queue mutexes when destroying the queue (LOCKDEBUG)
* make ns ctx pool per-device, so that it's deallocated properly on module
unload
* handle ctx allocation failure in ld_nvme_dobio()
* remove splbio() calls in ld_nvme_dobio() and sync, the paths are exercised
only for dump/shutdown, and that already disables interrupts
* free the ns ctx in ld_nvme_biodone() before calling lddone() to avoid
memory starvation, as lddone() can trigger another i/o request
* be more careful with using PR_WAITOK, the paths are called from interrupt
context and there we can't wait
To generate a diff of this commit:
cvs rdiff -u -r1.3 -r1.4 src/sys/dev/ic/ld_nvme.c
cvs rdiff -u -r1.8 -r1.9 src/sys/dev/ic/nvme.c
cvs rdiff -u -r1.2 -r1.3 src/sys/dev/ic/nvmevar.h
cvs rdiff -u -r1.13 -r1.14 src/sys/dev/pci/nvme_pci.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/ic/ld_nvme.c
diff -u src/sys/dev/ic/ld_nvme.c:1.3 src/sys/dev/ic/ld_nvme.c:1.4
--- src/sys/dev/ic/ld_nvme.c:1.3 Fri Sep 16 15:24:47 2016
+++ src/sys/dev/ic/ld_nvme.c Sun Sep 18 21:19:39 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: ld_nvme.c,v 1.3 2016/09/16 15:24:47 jdolecek Exp $ */
+/* $NetBSD: ld_nvme.c,v 1.4 2016/09/18 21:19:39 jdolecek Exp $ */
/*-
* Copyright (C) 2016 NONAKA Kimihiro <[email protected]>
@@ -26,7 +26,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ld_nvme.c,v 1.3 2016/09/16 15:24:47 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ld_nvme.c,v 1.4 2016/09/18 21:19:39 jdolecek Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -108,8 +108,8 @@ ld_nvme_attach(device_t parent, device_t
ld->sc_secsize = 1 << f->lbads;
ld->sc_secperunit = nsze;
- ld->sc_maxxfer = MAXPHYS;
- ld->sc_maxqueuecnt = naa->naa_qentries;
+ ld->sc_maxxfer = MAXPHYS; /* XXX set according to device */
+ ld->sc_maxqueuecnt = naa->naa_qentries; /* XXX set to max allowed by device, not current config */
ld->sc_start = ld_nvme_start;
ld->sc_dump = ld_nvme_dump;
ld->sc_flush = ld_nvme_flush;
@@ -156,9 +156,12 @@ ld_nvme_dobio(struct ld_nvme_softc *sc,
{
struct nvme_ns_context *ctx;
int error;
- int s;
+ int waitok = (bp != NULL && !cpu_softintr_p() && !cpu_intr_p());
+
+ ctx = nvme_ns_get_ctx(sc, waitok ? PR_WAITOK : PR_NOWAIT);
+ if (ctx == NULL)
+ return EAGAIN;
- ctx = nvme_ns_get_ctx(bp != NULL ? PR_WAITOK : PR_NOWAIT);
ctx->nnc_cookie = sc;
ctx->nnc_nsid = sc->sc_nsid;
ctx->nnc_done = ld_nvme_biodone;
@@ -168,14 +171,12 @@ ld_nvme_dobio(struct ld_nvme_softc *sc,
ctx->nnc_secsize = sc->sc_ld.sc_secsize;
ctx->nnc_blkno = blkno;
ctx->nnc_flags = dowrite ? 0 : NVME_NS_CTX_F_READ;
- if (bp == NULL) {
+ if (bp == NULL)
SET(ctx->nnc_flags, NVME_NS_CTX_F_POLL);
- s = splbio();
- }
+
error = nvme_ns_dobio(sc->sc_nvme, ctx);
- if (bp == NULL) {
- splx(s);
- }
+ if (error)
+ nvme_ns_put_ctx(sc, ctx);
return error;
}
@@ -187,6 +188,10 @@ ld_nvme_biodone(struct nvme_ns_context *
struct buf *bp = ctx->nnc_buf;
int status = NVME_CQE_SC(ctx->nnc_status);
+ /* free before processing to avoid starvation, lddone() could trigger
+ * another i/o request */
+ nvme_ns_put_ctx(sc, ctx);
+
if (bp != NULL) {
if (status != NVME_CQE_SC_SUCCESS) {
bp->b_error = EIO;
@@ -201,7 +206,6 @@ ld_nvme_biodone(struct nvme_ns_context *
aprint_error_dev(sc->sc_ld.sc_dv, "I/O error\n");
}
}
- nvme_ns_put_ctx(ctx);
}
static int
@@ -210,21 +214,23 @@ ld_nvme_flush(struct ld_softc *ld, int f
struct ld_nvme_softc *sc = device_private(ld->sc_dv);
struct nvme_ns_context *ctx;
int error;
- int s;
+ int waitok = (!ISSET(flags, LDFL_POLL)
+ && !cpu_softintr_p() && !cpu_intr_p());
+
+ ctx = nvme_ns_get_ctx(sc, waitok ? PR_WAITOK : PR_NOWAIT);
+ if (ctx == NULL)
+ return EAGAIN;
- ctx = nvme_ns_get_ctx((flags & LDFL_POLL) ? PR_NOWAIT : PR_WAITOK);
ctx->nnc_cookie = sc;
ctx->nnc_nsid = sc->sc_nsid;
ctx->nnc_done = ld_nvme_syncdone;
ctx->nnc_flags = 0;
- if (flags & LDFL_POLL) {
+ if (flags & LDFL_POLL)
SET(ctx->nnc_flags, NVME_NS_CTX_F_POLL);
- s = splbio();
- }
+
error = nvme_ns_sync(sc->sc_nvme, ctx);
- if (flags & LDFL_POLL) {
- splx(s);
- }
+ if (error)
+ nvme_ns_put_ctx(sc, ctx);
return error;
}
@@ -232,6 +238,7 @@ ld_nvme_flush(struct ld_softc *ld, int f
static void
ld_nvme_syncdone(struct nvme_ns_context *ctx)
{
+ struct ld_nvme_softc *sc = ctx->nnc_cookie;
- nvme_ns_put_ctx(ctx);
+ nvme_ns_put_ctx(sc, ctx);
}
Index: src/sys/dev/ic/nvme.c
diff -u src/sys/dev/ic/nvme.c:1.8 src/sys/dev/ic/nvme.c:1.9
--- src/sys/dev/ic/nvme.c:1.8 Sat Sep 17 19:52:16 2016
+++ src/sys/dev/ic/nvme.c Sun Sep 18 21:19:39 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: nvme.c,v 1.8 2016/09/17 19:52:16 jdolecek Exp $ */
+/* $NetBSD: nvme.c,v 1.9 2016/09/18 21:19:39 jdolecek Exp $ */
/* $OpenBSD: nvme.c,v 1.49 2016/04/18 05:59:50 dlg Exp $ */
/*
@@ -18,7 +18,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nvme.c,v 1.8 2016/09/17 19:52:16 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nvme.c,v 1.9 2016/09/18 21:19:39 jdolecek Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -41,7 +41,7 @@ __KERNEL_RCSID(0, "$NetBSD: nvme.c,v 1.8
#include <dev/ic/nvmeio.h>
int nvme_adminq_size = 128;
-int nvme_ioq_size = 128;
+int nvme_ioq_size = 1024;
static int nvme_print(void *, const char *);
@@ -156,18 +156,6 @@ nvme_write8(struct nvme_softc *sc, bus_s
#define nvme_barrier(_s, _r, _l, _f) \
bus_space_barrier((_s)->sc_iot, (_s)->sc_ioh, (_r), (_l), (_f))
-pool_cache_t nvme_ns_ctx_cache;
-ONCE_DECL(nvme_init_once);
-
-static int
-nvme_init(void)
-{
- nvme_ns_ctx_cache = pool_cache_init(sizeof(struct nvme_ns_context),
- 0, 0, 0, "nvme_ns_ctx", NULL, IPL_BIO, NULL, NULL, NULL);
- KASSERT(nvme_ns_ctx_cache != NULL);
- return 0;
-}
-
static void
nvme_version(struct nvme_softc *sc, uint32_t ver)
{
@@ -350,8 +338,6 @@ nvme_attach(struct nvme_softc *sc)
int ioq_entries = nvme_ioq_size;
int i;
- RUN_ONCE(&nvme_init_once, nvme_init);
-
reg = nvme_read4(sc, NVME_VS);
if (reg == 0xffffffff) {
aprint_error_dev(sc->sc_dev, "invalid mapping\n");
@@ -431,8 +417,20 @@ nvme_attach(struct nvme_softc *sc)
if (!sc->sc_use_mq)
nvme_write4(sc, NVME_INTMC, 1);
+ snprintf(sc->sc_ctxpoolname, sizeof(sc->sc_ctxpoolname),
+ "%s_ns_ctx", device_xname(sc->sc_dev));
+ sc->sc_ctxpool = pool_cache_init(sizeof(struct nvme_ns_context),
+ 0, 0, 0, sc->sc_ctxpoolname, NULL, IPL_BIO, NULL, NULL, NULL);
+ if (sc->sc_ctxpool == NULL) {
+ aprint_error_dev(sc->sc_dev, "unable to create ctx pool\n");
+ goto free_q;
+ }
+
+ /* probe subdevices */
sc->sc_namespaces = kmem_zalloc(sizeof(*sc->sc_namespaces) * sc->sc_nn,
KM_SLEEP);
+ if (sc->sc_namespaces == NULL)
+ goto free_q;
for (i = 0; i < sc->sc_nn; i++) {
memset(&naa, 0, sizeof(naa));
naa.naa_nsid = i + 1;
@@ -485,6 +483,9 @@ nvme_detach(struct nvme_softc *sc, int f
if (error)
return error;
+ /* from now on we are committed to detach, following will never fail */
+ pool_cache_destroy(sc->sc_ctxpool);
+
for (i = 0; i < sc->sc_nq; i++)
nvme_q_free(sc, sc->sc_q[i]);
kmem_free(sc->sc_q, sizeof(*sc->sc_q) * sc->sc_nq);
@@ -564,7 +565,8 @@ nvme_ns_identify(struct nvme_softc *sc,
KASSERT(nsid > 0);
ccb = nvme_ccb_get(sc->sc_admin_q);
- KASSERT(ccb != NULL);
+ if (ccb == NULL)
+ return EAGAIN;
mem = nvme_dmamem_alloc(sc, sizeof(*identify));
if (mem == NULL)
@@ -856,8 +858,9 @@ nvme_command_passthrough(struct nvme_sof
void *buf = NULL;
int error;
+ /* limit command size to maximum data transfer size */
if ((pt->buf == NULL && pt->len > 0) ||
- (pt->buf != NULL && pt->len == 0))
+ (pt->buf != NULL && (pt->len == 0 || pt->len > sc->sc_mdts)))
return EINVAL;
q = is_adminq ? sc->sc_admin_q : nvme_get_q(sc);
@@ -865,7 +868,8 @@ nvme_command_passthrough(struct nvme_sof
if (ccb == NULL)
return EBUSY;
- if (pt->buf != NULL && pt->len > 0) {
+ if (pt->buf != NULL) {
+ KASSERT(pt->len > 0);
buf = kmem_alloc(pt->len, KM_SLEEP);
if (buf == NULL) {
error = ENOMEM;
@@ -1022,35 +1026,42 @@ nvme_q_complete(struct nvme_softc *sc, s
{
struct nvme_ccb *ccb;
struct nvme_cqe *ring = NVME_DMA_KVA(q->q_cq_dmamem), *cqe;
- uint32_t head;
uint16_t flags;
int rv = 0;
- if (!mutex_tryenter(&q->q_cq_mtx))
- return -1;
+ mutex_enter(&q->q_cq_mtx);
nvme_dmamem_sync(sc, q->q_cq_dmamem, BUS_DMASYNC_POSTREAD);
- head = q->q_cq_head;
for (;;) {
- cqe = &ring[head];
+ cqe = &ring[q->q_cq_head];
flags = lemtoh16(&cqe->flags);
if ((flags & NVME_CQE_PHASE) != q->q_cq_phase)
break;
ccb = &q->q_ccbs[cqe->cid];
- ccb->ccb_done(q, ccb, cqe);
- if (++head >= q->q_entries) {
- head = 0;
+ if (++q->q_cq_head >= q->q_entries) {
+ q->q_cq_head = 0;
q->q_cq_phase ^= NVME_CQE_PHASE;
}
rv = 1;
+
+ /*
+ * Unlock the mutext before calling the ccb_done callback
+ * and re-lock afterwards. The callback triggers lddone()
+ * which schedules another i/o, and also calls nvme_ccb_put().
+ * Unlock/relock avoids possibility of deadlock.
+ */
+ mutex_exit(&q->q_cq_mtx);
+ ccb->ccb_done(q, ccb, cqe);
+ mutex_enter(&q->q_cq_mtx);
}
nvme_dmamem_sync(sc, q->q_cq_dmamem, BUS_DMASYNC_PREREAD);
if (rv)
- nvme_write4(sc, q->q_cqhdbl, q->q_cq_head = head);
+ nvme_write4(sc, q->q_cqhdbl, q->q_cq_head);
+
mutex_exit(&q->q_cq_mtx);
return rv;
@@ -1121,7 +1132,7 @@ nvme_q_create(struct nvme_softc *sc, str
struct nvme_ccb *ccb;
int rv;
- if (sc->sc_use_mq && sc->sc_intr_establish(sc, q->q_id, q))
+ if (sc->sc_use_mq && sc->sc_intr_establish(sc, q->q_id, q) != 0)
return 1;
ccb = nvme_ccb_get(sc->sc_admin_q);
@@ -1367,6 +1378,8 @@ static void
nvme_q_free(struct nvme_softc *sc, struct nvme_queue *q)
{
nvme_ccbs_free(q);
+ mutex_destroy(&q->q_sq_mtx);
+ mutex_destroy(&q->q_cq_mtx);
nvme_dmamem_sync(sc, q->q_cq_dmamem, BUS_DMASYNC_POSTREAD);
nvme_dmamem_sync(sc, q->q_sq_dmamem, BUS_DMASYNC_POSTWRITE);
nvme_dmamem_free(sc, q->q_cq_dmamem);
@@ -1380,46 +1393,38 @@ nvme_intr(void *xsc)
struct nvme_softc *sc = xsc;
int rv = 0;
- nvme_write4(sc, NVME_INTMS, 1);
-
+ /* INTx is level triggered, controller deasserts the interrupt only
+ * when we advance command queue head via write to the doorbell */
if (nvme_q_complete(sc, sc->sc_admin_q))
- rv = 1;
+ rv = 1;
if (sc->sc_q != NULL)
- if (nvme_q_complete(sc, sc->sc_q[0]))
- rv = 1;
-
- nvme_write4(sc, NVME_INTMC, 1);
+ if (nvme_q_complete(sc, sc->sc_q[0]))
+ rv = 1;
return rv;
}
int
-nvme_mq_msi_intr(void *xq)
+nvme_intr_msi(void *xq)
{
struct nvme_queue *q = xq;
- struct nvme_softc *sc = q->q_sc;
- int rv = 0;
-
- nvme_write4(sc, NVME_INTMS, 1U << q->q_id);
- if (nvme_q_complete(sc, q))
- rv = 1;
+ KASSERT(q && q->q_sc && q->q_sc->sc_softih
+ && q->q_sc->sc_softih[q->q_id]);
- nvme_write4(sc, NVME_INTMC, 1U << q->q_id);
+ /* MSI are edge triggered, so can handover processing to softint */
+ softint_schedule(q->q_sc->sc_softih[q->q_id]);
- return rv;
+ return 1;
}
-int
-nvme_mq_msix_intr(void *xq)
+void
+nvme_softintr_msi(void *xq)
{
struct nvme_queue *q = xq;
- int rv = 0;
-
- if (nvme_q_complete(q->q_sc, q))
- rv = 1;
+ struct nvme_softc *sc = q->q_sc;
- return rv;
+ nvme_q_complete(sc, q);
}
static struct nvme_dmamem *
Index: src/sys/dev/ic/nvmevar.h
diff -u src/sys/dev/ic/nvmevar.h:1.2 src/sys/dev/ic/nvmevar.h:1.3
--- src/sys/dev/ic/nvmevar.h:1.2 Sat Jun 4 16:11:51 2016
+++ src/sys/dev/ic/nvmevar.h Sun Sep 18 21:19:39 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: nvmevar.h,v 1.2 2016/06/04 16:11:51 nonaka Exp $ */
+/* $NetBSD: nvmevar.h,v 1.3 2016/09/18 21:19:39 jdolecek Exp $ */
/* $OpenBSD: nvmevar.h,v 1.8 2016/04/14 11:18:32 dlg Exp $ */
/*
@@ -95,17 +95,18 @@ struct nvme_softc {
uint16_t qid, struct nvme_queue *);
int (*sc_intr_disestablish)(struct nvme_softc *,
uint16_t qid);
- void **sc_ih;
+ void **sc_ih; /* interrupt handlers */
+ void **sc_softih; /* softintr handlers */
- u_int sc_rdy_to;
- size_t sc_mps;
- size_t sc_mdts;
- u_int sc_max_sgl;
+ u_int sc_rdy_to; /* RDY timeout */
+ size_t sc_mps; /* memory page size */
+ size_t sc_mdts; /* max data trasfer size */
+ u_int sc_max_sgl; /* max S/G segments */
struct nvm_identify_controller
sc_identify;
- u_int sc_nn;
+ u_int sc_nn; /* namespace count */
struct nvme_namespace *sc_namespaces;
bool sc_use_mq;
@@ -113,6 +114,9 @@ struct nvme_softc {
struct nvme_queue *sc_admin_q;
struct nvme_queue **sc_q;
+ pool_cache_t sc_ctxpool;
+ char sc_ctxpoolname[16]; /* pool wchan */
+
uint32_t sc_flags;
#define NVME_F_ATTACHED __BIT(0)
#define NVME_F_OPEN __BIT(1)
@@ -134,8 +138,8 @@ int nvme_attach(struct nvme_softc *);
int nvme_detach(struct nvme_softc *, int flags);
void nvme_childdet(device_t, device_t);
int nvme_intr(void *);
-int nvme_mq_msi_intr(void *);
-int nvme_mq_msix_intr(void *);
+int nvme_intr_msi(void *);
+void nvme_softintr_msi(void *);
static inline struct nvme_queue *
nvme_get_q(struct nvme_softc *sc)
@@ -174,10 +178,10 @@ struct nvme_ns_context {
int nnc_status;
};
-extern pool_cache_t nvme_ns_ctx_cache;
-
-#define nvme_ns_get_ctx(flags) pool_cache_get(nvme_ns_ctx_cache, (flags))
-#define nvme_ns_put_ctx(ctx) pool_cache_put(nvme_ns_ctx_cache, (ctx))
+#define nvme_ns_get_ctx(sc, flags) \
+ pool_cache_get((sc)->sc_nvme->sc_ctxpool, (flags))
+#define nvme_ns_put_ctx(sc, ctx) \
+ pool_cache_put((sc)->sc_nvme->sc_ctxpool, (ctx))
int nvme_ns_dobio(struct nvme_softc *, struct nvme_ns_context *);
int nvme_ns_sync(struct nvme_softc *, struct nvme_ns_context *);
Index: src/sys/dev/pci/nvme_pci.c
diff -u src/sys/dev/pci/nvme_pci.c:1.13 src/sys/dev/pci/nvme_pci.c:1.14
--- src/sys/dev/pci/nvme_pci.c:1.13 Sun Sep 18 11:58:35 2016
+++ src/sys/dev/pci/nvme_pci.c Sun Sep 18 21:19:39 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: nvme_pci.c,v 1.13 2016/09/18 11:58:35 jdolecek Exp $ */
+/* $NetBSD: nvme_pci.c,v 1.14 2016/09/18 21:19:39 jdolecek Exp $ */
/* $OpenBSD: nvme_pci.c,v 1.3 2016/04/14 11:18:32 dlg Exp $ */
/*
@@ -43,7 +43,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nvme_pci.c,v 1.13 2016/09/18 11:58:35 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nvme_pci.c,v 1.14 2016/09/18 21:19:39 jdolecek Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -64,7 +64,7 @@ __KERNEL_RCSID(0, "$NetBSD: nvme_pci.c,v
#include <dev/ic/nvmevar.h>
int nvme_pci_force_intx = 0;
-int nvme_pci_mpsafe = 0;
+int nvme_pci_mpsafe = 1;
int nvme_pci_mq = 1; /* INTx: ioq=1, MSI/MSI-X: ioq=ncpu */
#define NVME_PCI_BAR 0x10
@@ -192,9 +192,19 @@ nvme_pci_attach(device_t parent, device_
goto intr_release;
}
+ if (sc->sc_use_mq) {
+ sc->sc_softih = kmem_zalloc(
+ sizeof(*sc->sc_softih) * psc->psc_nintrs, KM_SLEEP);
+ if (sc->sc_softih == NULL) {
+ aprint_error_dev(self,
+ "unable to allocate softih memory\n");
+ goto intr_free;
+ }
+ }
+
if (nvme_attach(sc) != 0) {
/* error printed by nvme_attach() */
- goto intr_free;
+ goto softintr_free;
}
if (!pmf_device_register(self, NULL, NULL))
@@ -203,6 +213,11 @@ nvme_pci_attach(device_t parent, device_
SET(sc->sc_flags, NVME_F_ATTACHED);
return;
+softintr_free:
+ if (sc->sc_softih) {
+ kmem_free(sc->sc_softih,
+ sizeof(*sc->sc_softih) * psc->psc_nintrs);
+ }
intr_free:
kmem_free(sc->sc_ih, sizeof(*sc->sc_ih) * psc->psc_nintrs);
sc->sc_nq = 0;
@@ -228,6 +243,11 @@ nvme_pci_detach(device_t self, int flags
if (error)
return error;
+ if (sc->sc_softih) {
+ kmem_free(sc->sc_softih,
+ sizeof(*sc->sc_softih) * psc->psc_nintrs);
+ sc->sc_softih = NULL;
+ }
kmem_free(sc->sc_ih, sizeof(*sc->sc_ih) * psc->psc_nintrs);
pci_intr_release(psc->psc_pc, psc->psc_intrs, psc->psc_nintrs);
bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
@@ -274,13 +294,11 @@ nvme_pci_intr_establish(struct nvme_soft
device_xname(sc->sc_dev), qid);
}
ih_arg = q;
- if (pci_intr_type(psc->psc_pc, psc->psc_intrs[qid])
- == PCI_INTR_TYPE_MSIX)
- ih_func = nvme_mq_msix_intr;
- else
- ih_func = nvme_mq_msi_intr;
+ ih_func = nvme_intr_msi;
}
#endif /* __HAVE_PCI_MSI_MSIX */
+
+ /* establish hardware interrupt */
sc->sc_ih[qid] = pci_intr_establish_xname(psc->psc_pc,
psc->psc_intrs[qid], IPL_BIO, ih_func, ih_arg, intr_xname);
if (sc->sc_ih[qid] == NULL) {
@@ -288,6 +306,23 @@ nvme_pci_intr_establish(struct nvme_soft
"unable to establish %s interrupt\n", intr_xname);
return 1;
}
+
+ /* if MSI, establish also the software interrupt */
+ if (sc->sc_softih) {
+ sc->sc_softih[qid] = softint_establish(
+ SOFTINT_BIO|(nvme_pci_mpsafe ? SOFTINT_MPSAFE : 0),
+ nvme_softintr_msi, q);
+ if (sc->sc_softih[qid] == NULL) {
+ pci_intr_disestablish(psc->psc_pc, sc->sc_ih[qid]);
+ sc->sc_ih[qid] = NULL;
+
+ aprint_error_dev(sc->sc_dev,
+ "unable to establish %s soft interrupt\n",
+ intr_xname);
+ return 1;
+ }
+ }
+
intrstr = pci_intr_string(psc->psc_pc, psc->psc_intrs[qid], intrbuf,
sizeof(intrbuf));
if (!sc->sc_use_mq) {
@@ -324,11 +359,14 @@ nvme_pci_intr_disestablish(struct nvme_s
{
struct nvme_pci_softc *psc = (struct nvme_pci_softc *)sc;
- if (!sc->sc_use_mq && qid > 0)
- return 0;
-
+ KASSERT(sc->sc_use_mq || qid == NVME_ADMIN_Q);
KASSERT(sc->sc_ih[qid] != NULL);
+ if (sc->sc_softih) {
+ softint_disestablish(sc->sc_softih[qid]);
+ sc->sc_softih[qid] = NULL;
+ }
+
pci_intr_disestablish(psc->psc_pc, sc->sc_ih[qid]);
sc->sc_ih[qid] = NULL;