> From: "Theo de Raadt" <dera...@openbsd.org>
> Date: Tue, 06 Aug 2024 23:04:02 -0600
> 
> nvme resume is really crazy, since it does not believe the device is
> stopped, tries to use high-level operations to stop it and then restart
> it, but it ends up reusing queue structures from before.

Well, in sys/dev/ic/nvmereg.h I see this:

#define NVME_NSSR       0x0020  /* NVM Subsystem Reset (Optional) */

so a low-level hardware reset may not be possible...

> dv spent time in here, and I tried to figure it out also.  The code
> is highly suspect since it isn't doing the MINIMUM, which is:
> 
> at suspend time, ensure the soft state is good, and shut the hw down HARD.
> 
> On resume, reset the hardware HARD in the minimum fashion, and then
> reconfigure it to match the soft state.

My understanding of what dv@ was looking at was avoiding placing
nvme(4) in D3 since apparently some drives can't get out of that state
anymore.  So that may mean there is now way to completely whack the
device and that we need to keep it alive to send it certain power
management commands.

Anyway, I think you're right in thinking that nvme_intr() needs some
belt and suspenders.  In nvme_shutdown() we delete the "normal"
command queue, but nvme_intr() inconditionally looks at both of them.
Now nvme_shutdown() masks the interrupt and nvme_resume() unmasks it
only after it re-creates the "normal" command queue.  But I think
there are scenarios where we can get a spurious interrupt and it would
check a queue that isn't there.  So I think something like the diff
below would make sense.

Greg, does this fix your crash?


Index: dev/ic/nvme.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/nvme.c,v
diff -u -p -r1.121 nvme.c
--- dev/ic/nvme.c       13 Jul 2024 08:59:41 -0000      1.121
+++ dev/ic/nvme.c       7 Aug 2024 09:31:14 -0000
@@ -418,12 +418,14 @@ nvme_attach(struct nvme_softc *sc)
 
 free_q:
        nvme_q_free(sc, sc->sc_q);
+       sc->sc_q = NULL;
 disable:
        nvme_disable(sc);
 free_ccbs:
        nvme_ccbs_free(sc, nccbs);
 free_admin_q:
        nvme_q_free(sc, sc->sc_admin_q);
+       sc->sc_admin_q = NULL;
 
        return (1);
 }
@@ -463,6 +465,7 @@ nvme_resume(struct nvme_softc *sc)
 
 free_q:
        nvme_q_free(sc, sc->sc_q);
+       sc->sc_q = NULL;
 disable:
        nvme_disable(sc);
 
@@ -531,6 +534,7 @@ nvme_shutdown(struct nvme_softc *sc)
                printf("%s: unable to delete q, disabling\n", DEVNAME(sc));
                goto disable;
        }
+       sc->sc_q = NULL;
 
        cc = nvme_read4(sc, NVME_CC);
        CLR(cc, NVME_CC_SHN_MASK);
@@ -1552,9 +1556,9 @@ nvme_intr(void *xsc)
        struct nvme_softc *sc = xsc;
        int rv = 0;
 
-       if (nvme_q_complete(sc, sc->sc_q))
+       if (sc->sc_q && nvme_q_complete(sc, sc->sc_q))
                rv = 1;
-       if (nvme_q_complete(sc, sc->sc_admin_q))
+       if (sc->sc_admin_q && nvme_q_complete(sc, sc->sc_admin_q))
                rv = 1;
 
        return (rv);


Reply via email to