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 <non...@netbsd.org>
@@ -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);

Reply via email to