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);