support DIOCSCACHE + DKCACHE_WRITE if volatile write cache is present

fix the Get Features call for DIOCGCACHE to actually retrieve the current
value properly
This commit is contained in:
jdolecek 2018-12-01 15:07:58 +00:00
parent 6bb18810c6
commit b7a0ea19e0
4 changed files with 124 additions and 9 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: ld_nvme.c,v 1.20 2018/04/18 10:11:45 nonaka Exp $ */
/* $NetBSD: ld_nvme.c,v 1.21 2018/12/01 15:07:58 jdolecek Exp $ */
/*-
* Copyright (C) 2016 NONAKA Kimihiro <nonaka@netbsd.org>
@ -26,7 +26,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ld_nvme.c,v 1.20 2018/04/18 10:11:45 nonaka Exp $");
__KERNEL_RCSID(0, "$NetBSD: ld_nvme.c,v 1.21 2018/12/01 15:07:58 jdolecek Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -199,6 +199,14 @@ ld_nvme_getcache(struct ld_softc *ld, int *addr)
return nvme_admin_getcache(sc->sc_nvme, addr);
}
static int
ld_nvme_setcache(struct ld_softc *ld, int addr)
{
struct ld_nvme_softc *sc = device_private(ld->sc_dv);
return nvme_admin_setcache(sc->sc_nvme, addr);
}
static int
ld_nvme_ioctl(struct ld_softc *ld, u_long cmd, void *addr, int32_t flag, bool poll)
{
@ -213,6 +221,10 @@ ld_nvme_ioctl(struct ld_softc *ld, u_long cmd, void *addr, int32_t flag, bool po
error = ld_nvme_getcache(ld, (int *)addr);
break;
case DIOCSCACHE:
error = ld_nvme_setcache(ld, *(int *)addr);
break;
default:
error = EPASSTHROUGH;
break;

View File

@ -1,4 +1,4 @@
/* $NetBSD: nvme.c,v 1.40 2018/12/01 08:03:44 jdolecek Exp $ */
/* $NetBSD: nvme.c,v 1.41 2018/12/01 15:07:58 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.40 2018/12/01 08:03:44 jdolecek Exp $");
__KERNEL_RCSID(0, "$NetBSD: nvme.c,v 1.41 2018/12/01 15:07:58 jdolecek Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -902,6 +902,7 @@ nvme_getcache_fill(struct nvme_queue *q, struct nvme_ccb *ccb, void *slot)
sqe->opcode = NVM_ADMIN_GET_FEATURES;
htolem32(&sqe->cdw10, NVM_FEATURE_VOLATILE_WRITE_CACHE);
htolem32(&sqe->cdw11, NVM_VOLATILE_WRITE_CACHE_WCE);
}
static void
@ -922,7 +923,7 @@ nvme_getcache_done(struct nvme_queue *q, struct nvme_ccb *ccb,
*/
result = DKCACHE_FUA;
if (cdw0 & NVME_CQE_CDW0_VWC_WCE)
if (cdw0 & NVM_VOLATILE_WRITE_CACHE_WCE)
result |= DKCACHE_WRITE;
/*
@ -930,6 +931,14 @@ nvme_getcache_done(struct nvme_queue *q, struct nvme_ccb *ccb,
* settable.
*/
result |= DKCACHE_WCHANGE;
/*
* ONCS field indicates whether the optional SAVE is also
* supported for Set Features. According to spec v1.3,
* Volatile Write Cache however doesn't support persistency
* across power cycle/reset.
*/
} else {
result = -1;
}
@ -939,6 +948,95 @@ nvme_getcache_done(struct nvme_queue *q, struct nvme_ccb *ccb,
nvme_ccb_put(q, ccb);
}
struct nvme_setcache_state {
int dkcache;
int result;
};
static bool
nvme_setcache_finished(void *xc)
{
struct nvme_setcache_state *st = xc;
return (st->result != 0);
}
static void
nvme_setcache_fill(struct nvme_queue *q, struct nvme_ccb *ccb, void *slot)
{
struct nvme_sqe *sqe = slot;
struct nvme_setcache_state *st = ccb->ccb_cookie;
sqe->opcode = NVM_ADMIN_SET_FEATURES;
htolem32(&sqe->cdw10, NVM_FEATURE_VOLATILE_WRITE_CACHE);
if (st->dkcache & DKCACHE_WRITE)
htolem32(&sqe->cdw11, NVM_VOLATILE_WRITE_CACHE_WCE);
}
static void
nvme_setcache_done(struct nvme_queue *q, struct nvme_ccb *ccb,
struct nvme_cqe *cqe)
{
struct nvme_setcache_state *st = ccb->ccb_cookie;
uint16_t status = NVME_CQE_SC(lemtoh16(&cqe->flags));
if (status == NVME_CQE_SC_SUCCESS) {
st->result = 1;
} else {
st->result = -1;
}
nvme_ccb_put(q, ccb);
}
/*
* Set status of volatile write cache. Always asynchronous.
*/
int
nvme_admin_setcache(struct nvme_softc *sc, int dkcache)
{
struct nvme_ccb *ccb;
struct nvme_queue *q = sc->sc_admin_q;
int error;
struct nvme_setcache_state st;
if (!nvme_has_volatile_write_cache(sc)) {
/* cache simply not present */
return EOPNOTSUPP;
}
if (dkcache & ~(DKCACHE_WRITE)) {
/* unsupported parameters */
return EOPNOTSUPP;
}
ccb = nvme_ccb_get(q, true);
KASSERT(ccb != NULL);
memset(&st, 0, sizeof(st));
st.dkcache = dkcache;
ccb->ccb_done = nvme_setcache_done;
ccb->ccb_cookie = &st;
/* namespace context */
ccb->nnc_flags = 0;
ccb->nnc_done = NULL;
nvme_q_submit(sc, q, ccb, nvme_setcache_fill);
/* wait for completion */
nvme_q_wait_complete(sc, q, nvme_setcache_finished, &st);
KASSERT(st.result != 0);
if (st.result > 0)
error = 0;
else
error = EINVAL;
return error;
}
void
nvme_ns_free(struct nvme_softc *sc, uint16_t nsid)
{

View File

@ -1,4 +1,4 @@
/* $NetBSD: nvmereg.h,v 1.11 2018/04/18 10:10:26 nonaka Exp $ */
/* $NetBSD: nvmereg.h,v 1.12 2018/12/01 15:07:58 jdolecek Exp $ */
/* $OpenBSD: nvmereg.h,v 1.10 2016/04/14 11:18:32 dlg Exp $ */
/*
@ -230,7 +230,6 @@ NVME_CTASSERT(sizeof(struct nvme_sqe_io) == 64, "bad size for nvme_sqe_io");
struct nvme_cqe {
uint32_t cdw0;
#define NVME_CQE_CDW0_VWC_WCE __BIT(1) /* Volatile Write Cache Enable */
uint32_t _reserved;
@ -369,6 +368,10 @@ NVME_CTASSERT(sizeof(struct nvme_cqe) == 16, "bad size for nvme_cqe");
/* 0x84-0xBF - command set specific (reserved) */
/* 0xC0-0xFF - vendor specific */
#define NVM_SET_FEATURES_SV __BIT(31) /* Persist */
#define NVM_VOLATILE_WRITE_CACHE_WCE __BIT(0) /* Write Cache Enable */
/* Power State Descriptor Data */
struct nvm_identify_psd {
uint16_t mp; /* Max Power */
@ -514,8 +517,9 @@ struct nvm_identify_controller {
uint32_t nn; /* Number of Namespaces */
uint16_t oncs; /* Optional NVM Command Support */
#define NVME_ID_CTRLR_ONCS_TIMESTAMP __BIT(6)
#define NVME_ID_CTRLR_ONCS_RESERVATION __BIT(5)
#define NVME_ID_CTRLR_ONCS_SET_FEATURES __BIT(4)
#define NVME_ID_CTRLR_ONCS_SAVE __BIT(4)
#define NVME_ID_CTRLR_ONCS_WRITE_ZERO __BIT(3)
#define NVME_ID_CTRLR_ONCS_DSM __BIT(2)
#define NVME_ID_CTRLR_ONCS_WRITE_UNC __BIT(1)

View File

@ -1,4 +1,4 @@
/* $NetBSD: nvmevar.h,v 1.17 2018/04/19 21:50:08 christos Exp $ */
/* $NetBSD: nvmevar.h,v 1.18 2018/12/01 15:07:58 jdolecek Exp $ */
/* $OpenBSD: nvmevar.h,v 1.8 2016/04/14 11:18:32 dlg Exp $ */
/*
@ -186,3 +186,4 @@ 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, int);
int nvme_admin_getcache(struct nvme_softc *, int *);
int nvme_admin_setcache(struct nvme_softc *, int);