Module Name: src
Committed By: jdolecek
Date: Tue Feb 28 20:53:50 UTC 2017
Modified Files:
src/sys/dev/ic: ld_nvme.c nvme.c nvmereg.h nvmevar.h
Log Message:
implement DIOCGCACHE
To generate a diff of this commit:
cvs rdiff -u -r1.12 -r1.13 src/sys/dev/ic/ld_nvme.c
cvs rdiff -u -r1.24 -r1.25 src/sys/dev/ic/nvme.c
cvs rdiff -u -r1.7 -r1.8 src/sys/dev/ic/nvmereg.h
cvs rdiff -u -r1.11 -r1.12 src/sys/dev/ic/nvmevar.h
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.12 src/sys/dev/ic/ld_nvme.c:1.13
--- src/sys/dev/ic/ld_nvme.c:1.12 Mon Feb 27 21:48:34 2017
+++ src/sys/dev/ic/ld_nvme.c Tue Feb 28 20:53:50 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: ld_nvme.c,v 1.12 2017/02/27 21:48:34 jdolecek Exp $ */
+/* $NetBSD: ld_nvme.c,v 1.13 2017/02/28 20:53:50 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.12 2017/02/27 21:48:34 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ld_nvme.c,v 1.13 2017/02/28 20:53:50 jdolecek Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -49,6 +49,14 @@ struct ld_nvme_softc {
struct nvme_softc *sc_nvme;
uint16_t sc_nsid;
+
+ /* getcache handling */
+ kmutex_t sc_getcache_lock;
+ kcondvar_t sc_getcache_cv;
+ kcondvar_t sc_getcache_ready_cv;
+ bool sc_getcache_waiting;
+ bool sc_getcache_ready;
+ int sc_getcache_result;
};
static int ld_nvme_match(device_t, cfdata_t, void *);
@@ -61,10 +69,12 @@ CFATTACH_DECL_NEW(ld_nvme, sizeof(struct
static int ld_nvme_start(struct ld_softc *, struct buf *);
static int ld_nvme_dump(struct ld_softc *, void *, int, int);
static int ld_nvme_flush(struct ld_softc *, bool);
+static int ld_nvme_getcache(struct ld_softc *, int *);
static int ld_nvme_ioctl(struct ld_softc *, u_long, void *, int32_t, bool);
-static void ld_nvme_biodone(void *, struct buf *, uint16_t);
-static void ld_nvme_syncdone(void *, struct buf *, uint16_t);
+static void ld_nvme_biodone(void *, struct buf *, uint16_t, uint32_t);
+static void ld_nvme_syncdone(void *, struct buf *, uint16_t, uint32_t);
+static void ld_nvme_getcache_done(void *, struct buf *, uint16_t, uint32_t);
static int
ld_nvme_match(device_t parent, cfdata_t match, void *aux)
@@ -93,6 +103,10 @@ ld_nvme_attach(device_t parent, device_t
sc->sc_nvme = nsc;
sc->sc_nsid = naa->naa_nsid;
+ mutex_init(&sc->sc_getcache_lock, MUTEX_DEFAULT, IPL_SOFTBIO);
+ cv_init(&sc->sc_getcache_cv, "nvmegcq");
+ cv_init(&sc->sc_getcache_ready_cv, "nvmegcr");
+
aprint_naive("\n");
aprint_normal("\n");
@@ -159,7 +173,7 @@ ld_nvme_dump(struct ld_softc *ld, void *
}
static void
-ld_nvme_biodone(void *xc, struct buf *bp, uint16_t cmd_status)
+ld_nvme_biodone(void *xc, struct buf *bp, uint16_t cmd_status, uint32_t cdw0)
{
struct ld_nvme_softc *sc = xc;
uint16_t status = NVME_CQE_SC(cmd_status);
@@ -190,6 +204,104 @@ ld_nvme_flush(struct ld_softc *ld, bool
ld_nvme_syncdone);
}
+static void
+ld_nvme_syncdone(void *xc, struct buf *bp, uint16_t cmd_status, uint32_t cdw0)
+{
+ /* nothing to do */
+}
+
+static int
+ld_nvme_getcache(struct ld_softc *ld, int *addr)
+{
+ int error;
+ struct ld_nvme_softc *sc = device_private(ld->sc_dv);
+
+ *addr = 0;
+
+ if (!nvme_has_volatile_write_cache(sc->sc_nvme)) {
+ /* cache simply not present */
+ return 0;
+ }
+
+ /*
+ * This is admin queue request. The queue is relatively limited in size,
+ * and this is not performance critical call, so have at most one pending
+ * cache request at a time to avoid spurious EWOULDBLOCK failures.
+ */
+ mutex_enter(&sc->sc_getcache_lock);
+ while (sc->sc_getcache_waiting) {
+ error = cv_wait_sig(&sc->sc_getcache_cv, &sc->sc_getcache_lock);
+ if (error)
+ goto out;
+ }
+ sc->sc_getcache_waiting = true;
+ sc->sc_getcache_ready = false;
+ mutex_exit(&sc->sc_getcache_lock);
+
+ error = nvme_admin_getcache(sc->sc_nvme, sc, ld_nvme_getcache_done);
+ if (error) {
+ mutex_enter(&sc->sc_getcache_lock);
+ goto out;
+ }
+
+ mutex_enter(&sc->sc_getcache_lock);
+ while (!sc->sc_getcache_ready) {
+ error = cv_wait_sig(&sc->sc_getcache_ready_cv,
+ &sc->sc_getcache_lock);
+ if (error)
+ goto out;
+ }
+
+ KDASSERT(sc->sc_getcache_ready);
+
+ if (sc->sc_getcache_result >= 0)
+ *addr |= sc->sc_getcache_result;
+ else
+ error = EINVAL;
+
+ out:
+ sc->sc_getcache_waiting = false;
+
+ /* wake one of eventual waiters */
+ cv_signal(&sc->sc_getcache_cv);
+
+ mutex_exit(&sc->sc_getcache_lock);
+
+ return error;
+}
+
+static void
+ld_nvme_getcache_done(void *xc, struct buf *bp, uint16_t cmd_status, uint32_t cdw0)
+{
+ struct ld_nvme_softc *sc = xc;
+ uint16_t status = NVME_CQE_SC(cmd_status);
+ int result;
+
+ if (status == NVME_CQE_SC_SUCCESS) {
+ result = 0;
+
+ if (cdw0 & NVME_CQE_CDW0_VWC_WCE)
+ result |= DKCACHE_WRITE;
+
+ /*
+ * If volatile write cache is present, the flag shall also be
+ * settable.
+ */
+ result |= DKCACHE_WCHANGE;
+ } else {
+ result = -1;
+ }
+
+ mutex_enter(&sc->sc_getcache_lock);
+ sc->sc_getcache_result = result;
+ sc->sc_getcache_ready = true;
+
+ /* wake up the waiter */
+ cv_signal(&sc->sc_getcache_ready_cv);
+
+ mutex_exit(&sc->sc_getcache_lock);
+}
+
static int
ld_nvme_ioctl(struct ld_softc *ld, u_long cmd, void *addr, int32_t flag, bool poll)
{
@@ -200,6 +312,10 @@ ld_nvme_ioctl(struct ld_softc *ld, u_lon
error = ld_nvme_flush(ld, poll);
break;
+ case DIOCGCACHE:
+ error = ld_nvme_getcache(ld, (int *)addr);
+ break;
+
default:
error = EPASSTHROUGH;
break;
@@ -208,12 +324,6 @@ ld_nvme_ioctl(struct ld_softc *ld, u_lon
return error;
}
-static void
-ld_nvme_syncdone(void *xc, struct buf *bp, uint16_t cmd_status)
-{
- /* nothing to do */
-}
-
MODULE(MODULE_CLASS_DRIVER, ld_nvme, "ld,nvme");
#ifdef _MODULE
Index: src/sys/dev/ic/nvme.c
diff -u src/sys/dev/ic/nvme.c:1.24 src/sys/dev/ic/nvme.c:1.25
--- src/sys/dev/ic/nvme.c:1.24 Mon Feb 13 11:11:32 2017
+++ src/sys/dev/ic/nvme.c Tue Feb 28 20:53:50 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: nvme.c,v 1.24 2017/02/13 11:11:32 nonaka Exp $ */
+/* $NetBSD: nvme.c,v 1.25 2017/02/28 20:53:50 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.24 2017/02/13 11:11:32 nonaka Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nvme.c,v 1.25 2017/02/28 20:53:50 jdolecek Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -99,6 +99,10 @@ static void nvme_ns_sync_fill(struct nvm
void *);
static void nvme_ns_sync_done(struct nvme_queue *, struct nvme_ccb *,
struct nvme_cqe *);
+static void nvme_getcache_fill(struct nvme_queue *, struct nvme_ccb *,
+ void *);
+static void nvme_getcache_done(struct nvme_queue *, struct nvme_ccb *,
+ struct nvme_cqe *);
static void nvme_pt_fill(struct nvme_queue *, struct nvme_ccb *,
void *);
@@ -753,7 +757,18 @@ nvme_ns_io_done(struct nvme_queue *q, st
bus_dmamap_unload(sc->sc_dmat, dmap);
nvme_ccb_put(q, ccb);
- nnc_done(nnc_cookie, bp, lemtoh16(&cqe->flags));
+ nnc_done(nnc_cookie, bp, lemtoh16(&cqe->flags), lemtoh32(&cqe->cdw0));
+}
+
+/*
+ * If there is no volatile write cache, it makes no sense to issue
+ * flush commands or query for the status.
+ */
+bool
+nvme_has_volatile_write_cache(struct nvme_softc *sc)
+{
+ /* sc_identify is filled during attachment */
+ return ((sc->sc_identify.vwc & NVME_ID_CTRLR_VWC_PRESENT) != 0);
}
int
@@ -803,7 +818,52 @@ nvme_ns_sync_done(struct nvme_queue *q,
nvme_ccb_put(q, ccb);
- nnc_done(cookie, NULL, lemtoh16(&cqe->flags));
+ nnc_done(cookie, NULL, lemtoh16(&cqe->flags), lemtoh32(&cqe->cdw0));
+}
+
+/*
+ * Get status of volatile write cache. Always asynchronous.
+ */
+int
+nvme_admin_getcache(struct nvme_softc *sc, void *cookie, nvme_nnc_done nnc_done)
+{
+ struct nvme_ccb *ccb;
+ struct nvme_queue *q = sc->sc_admin_q;
+
+ ccb = nvme_ccb_get(q);
+ if (ccb == NULL)
+ return EAGAIN;
+
+ ccb->ccb_done = nvme_getcache_done;
+ ccb->ccb_cookie = cookie;
+
+ /* namespace context */
+ ccb->nnc_flags = 0;
+ ccb->nnc_done = nnc_done;
+
+ nvme_q_submit(sc, q, ccb, nvme_getcache_fill);
+ return 0;
+}
+
+static void
+nvme_getcache_fill(struct nvme_queue *q, struct nvme_ccb *ccb, void *slot)
+{
+ struct nvme_sqe *sqe = slot;
+
+ sqe->opcode = NVM_ADMIN_GET_FEATURES;
+ sqe->cdw10 = NVM_FEATURE_VOLATILE_WRITE_CACHE;
+}
+
+static void
+nvme_getcache_done(struct nvme_queue *q, struct nvme_ccb *ccb,
+ struct nvme_cqe *cqe)
+{
+ void *cookie = ccb->ccb_cookie;
+ nvme_nnc_done nnc_done = ccb->nnc_done;
+
+ nvme_ccb_put(q, ccb);
+
+ nnc_done(cookie, NULL, lemtoh16(&cqe->flags), lemtoh32(&cqe->cdw0));
}
void
@@ -1300,7 +1360,7 @@ nvme_get_number_of_queues(struct nvme_so
memset(&pt, 0, sizeof(pt));
pt.cmd.opcode = NVM_ADMIN_GET_FEATURES;
- pt.cmd.cdw10 = 7 /*NVME_FEAT_NUMBER_OF_QUEUES*/;
+ pt.cmd.cdw10 = NVM_FEATURE_NUMBER_OF_QUEUES;
ccb->ccb_done = nvme_pt_done;
ccb->ccb_cookie = &pt;
Index: src/sys/dev/ic/nvmereg.h
diff -u src/sys/dev/ic/nvmereg.h:1.7 src/sys/dev/ic/nvmereg.h:1.8
--- src/sys/dev/ic/nvmereg.h:1.7 Mon Feb 13 11:11:32 2017
+++ src/sys/dev/ic/nvmereg.h Tue Feb 28 20:53:50 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: nvmereg.h,v 1.7 2017/02/13 11:11:32 nonaka Exp $ */
+/* $NetBSD: nvmereg.h,v 1.8 2017/02/28 20:53:50 jdolecek Exp $ */
/* $OpenBSD: nvmereg.h,v 1.10 2016/04/14 11:18:32 dlg Exp $ */
/*
@@ -223,6 +223,7 @@ struct nvme_sqe_io {
struct nvme_cqe {
uint32_t cdw0;
+#define NVME_CQE_CDW0_VWC_WCE __BIT(1) /* Volatile Write Cache Enable */
uint32_t _reserved;
@@ -315,6 +316,10 @@ struct nvme_cqe {
#define NVM_CMD_COMPARE 0x05 /* Compare */
#define NVM_CMD_DSM 0x09 /* Dataset Management */
+/* Features for GET/SET FEATURES */
+#define NVM_FEATURE_VOLATILE_WRITE_CACHE 0x06 /* optional */
+#define NVM_FEATURE_NUMBER_OF_QUEUES 0x07 /* mandatory */
+
/* Power State Descriptor Data */
struct nvm_identify_psd {
uint16_t mp; /* Max Power */
Index: src/sys/dev/ic/nvmevar.h
diff -u src/sys/dev/ic/nvmevar.h:1.11 src/sys/dev/ic/nvmevar.h:1.12
--- src/sys/dev/ic/nvmevar.h:1.11 Tue Nov 1 14:39:38 2016
+++ src/sys/dev/ic/nvmevar.h Tue Feb 28 20:53:50 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: nvmevar.h,v 1.11 2016/11/01 14:39:38 jdolecek Exp $ */
+/* $NetBSD: nvmevar.h,v 1.12 2017/02/28 20:53:50 jdolecek Exp $ */
/* $OpenBSD: nvmevar.h,v 1.8 2016/04/14 11:18:32 dlg Exp $ */
/*
@@ -38,7 +38,7 @@ struct nvme_dmamem {
struct nvme_softc;
struct nvme_queue;
-typedef void (*nvme_nnc_done)(void *, struct buf *, uint16_t);
+typedef void (*nvme_nnc_done)(void *, struct buf *, uint16_t, uint32_t);
struct nvme_ccb {
SIMPLEQ_ENTRY(nvme_ccb) ccb_entry;
@@ -180,3 +180,5 @@ void nvme_ns_free(struct nvme_softc *, u
int nvme_ns_dobio(struct nvme_softc *, uint16_t, void *,
struct buf *, void *, size_t, int, daddr_t, int, nvme_nnc_done);
int nvme_ns_sync(struct nvme_softc *, uint16_t, void *, int, nvme_nnc_done);
+bool nvme_has_volatile_write_cache(struct nvme_softc *);
+int nvme_admin_getcache(struct nvme_softc *, void *, nvme_nnc_done);