diff --git a/sys/dev/nvme/nvme_private.h b/sys/dev/nvme/nvme_private.h --- a/sys/dev/nvme/nvme_private.h +++ b/sys/dev/nvme/nvme_private.h @@ -168,6 +168,7 @@ sbintime_t deadline; bool timer_armed; enum nvme_recovery recovery_state; + uint32_t in_isr; uint32_t num_entries; uint32_t num_trackers; diff --git a/sys/dev/nvme/nvme_qpair.c b/sys/dev/nvme/nvme_qpair.c --- a/sys/dev/nvme/nvme_qpair.c +++ b/sys/dev/nvme/nvme_qpair.c @@ -538,6 +538,19 @@ int done = 0; bool in_panic = dumping || SCHEDULER_STOPPED(); + /* + * If we're not panicing, make sure we're the only one here. If we are + * panicing, then we need to poll and we know we're the only thread + * that's running, so its safe since we cope with re-entering this + * routine in that case already (since the other thread in that routine + * won't be scheduled again). This protects the hardware completion + * queue to ensure that there's only one reader of it at any given time. + */ + if (__predict_true(!in_panic)) { + if (!atomic_cmpset_32(&qpair->in_isr, 0, 1)) + return (false); + } + /* * qpair is not enabled, likely because a controller reset is in * progress. Ignore the interrupt - any I/O that was associated with @@ -548,6 +561,7 @@ */ if (qpair->recovery_state != RECOVERY_NONE) { qpair->num_ignored++; + atomic_store_32(&qpair->in_isr, 0); return (false); } @@ -673,6 +687,7 @@ qpair->cq_hdbl_off, qpair->cq_head); } + atomic_store_32(&qpair->in_isr, 0); return (done != 0); }