diff options
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/man/man7d/nvme.7d | 8 | ||||
-rw-r--r-- | usr/src/uts/common/io/nvme/nvme.c | 121 | ||||
-rw-r--r-- | usr/src/uts/common/io/nvme/nvme.conf | 7 | ||||
-rw-r--r-- | usr/src/uts/common/io/nvme/nvme_reg.h | 9 | ||||
-rw-r--r-- | usr/src/uts/common/io/nvme/nvme_var.h | 3 |
5 files changed, 117 insertions, 31 deletions
diff --git a/usr/src/man/man7d/nvme.7d b/usr/src/man/man7d/nvme.7d index 7742fc22f6..fafe4ed82e 100644 --- a/usr/src/man/man7d/nvme.7d +++ b/usr/src/man/man7d/nvme.7d @@ -9,9 +9,9 @@ .\" http://www.illumos.org/license/CDDL. .\" .\" -.\" Copyright 2015 Nexenta Systems, Inc. All rights reserved. +.\" Copyright 2016 Nexenta Systems, Inc. All rights reserved. .\" -.Dd July 20, 2015 +.Dd May 13, 2016 .Dt NVME 7D .Os .Sh NAME @@ -63,6 +63,10 @@ the driver. Asynchronous events are used to report error conditions. The driver will never use more asynchronous events than this value, or what the hardware supports if it is less, or what 1/10th of the admin queue length if it is less. +.It Va volatile-write-cache-enable +This property can be set to 0 to disable the volatile write cache, if +the hardware supports it. +The default setting is 1, which enables the volatile write cache. .El . .Sh FILES diff --git a/usr/src/uts/common/io/nvme/nvme.c b/usr/src/uts/common/io/nvme/nvme.c index 1ea72fc792..391a6500b7 100644 --- a/usr/src/uts/common/io/nvme/nvme.c +++ b/usr/src/uts/common/io/nvme/nvme.c @@ -147,13 +147,14 @@ * - io-queue-len: the maximum length of the I/O queues (16-65536) * - async-event-limit: the maximum number of asynchronous event requests to be * posted by the driver + * - volatile-write-cache-enable: can be set to 0 to disable the volatile write + * cache * * * TODO: * - figure out sane default for I/O queue depth reported to blkdev * - polled I/O support to support kernel core dumping * - FMA handling of media errors - * - support for the Volatile Write Cache * - support for devices supporting very large I/O requests using chained PRPs * - support for querying log pages from user space * - support for configuring hardware parameters like interrupt coalescing @@ -227,6 +228,9 @@ static void nvme_abort_cmd(nvme_cmd_t *); static int nvme_async_event(nvme_t *); static void *nvme_get_logpage(nvme_t *, uint8_t, ...); static void *nvme_identify(nvme_t *, uint32_t); +static boolean_t nvme_set_features(nvme_t *, uint32_t, uint8_t, uint32_t, + uint32_t *); +static boolean_t nvme_write_cache_set(nvme_t *, boolean_t); static int nvme_set_nqueues(nvme_t *, uint16_t); static void nvme_free_dma(nvme_dma_t *); @@ -1611,36 +1615,81 @@ fail: return (buf); } -static int -nvme_set_nqueues(nvme_t *nvme, uint16_t nqueues) +static boolean_t +nvme_set_features(nvme_t *nvme, uint32_t nsid, uint8_t feature, uint32_t val, + uint32_t *res) { + _NOTE(ARGUNUSED(nsid)); nvme_cmd_t *cmd = nvme_alloc_cmd(nvme, KM_SLEEP); - nvme_nqueue_t nq = { 0 }; + boolean_t ret = B_FALSE; - nq.b.nq_nsq = nq.b.nq_ncq = nqueues - 1; + ASSERT(res != NULL); cmd->nc_sqid = 0; cmd->nc_callback = nvme_wakeup_cmd; cmd->nc_sqe.sqe_opc = NVME_OPC_SET_FEATURES; - cmd->nc_sqe.sqe_cdw10 = NVME_FEAT_NQUEUES; - cmd->nc_sqe.sqe_cdw11 = nq.r; + cmd->nc_sqe.sqe_cdw10 = feature; + cmd->nc_sqe.sqe_cdw11 = val; + + switch (feature) { + case NVME_FEAT_WRITE_CACHE: + if (!nvme->n_write_cache_present) + goto fail; + break; + + case NVME_FEAT_NQUEUES: + break; + + default: + goto fail; + } if (nvme_admin_cmd(cmd, nvme_admin_cmd_timeout) != DDI_SUCCESS) { dev_err(nvme->n_dip, CE_WARN, - "!nvme_admin_cmd failed for SET FEATURES (NQUEUES)"); - return (0); + "!nvme_admin_cmd failed for SET FEATURES"); + return (ret); } if (nvme_check_cmd_status(cmd)) { dev_err(nvme->n_dip, CE_WARN, - "!SET FEATURES (NQUEUES) failed with sct = %x, sc = %x", - cmd->nc_cqe.cqe_sf.sf_sct, cmd->nc_cqe.cqe_sf.sf_sc); - nvme_free_cmd(cmd); - return (0); + "!SET FEATURES %d failed with sct = %x, sc = %x", + feature, cmd->nc_cqe.cqe_sf.sf_sct, + cmd->nc_cqe.cqe_sf.sf_sc); + goto fail; } - nq.r = cmd->nc_cqe.cqe_dw0; + *res = cmd->nc_cqe.cqe_dw0; + ret = B_TRUE; + +fail: nvme_free_cmd(cmd); + return (ret); +} + +static boolean_t +nvme_write_cache_set(nvme_t *nvme, boolean_t enable) +{ + nvme_write_cache_t nwc = { 0 }; + + if (enable) + nwc.b.wc_wce = 1; + + if (!nvme_set_features(nvme, 0, NVME_FEAT_WRITE_CACHE, nwc.r, &nwc.r)) + return (B_FALSE); + + return (B_TRUE); +} + +static int +nvme_set_nqueues(nvme_t *nvme, uint16_t nqueues) +{ + nvme_nqueue_t nq = { 0 }; + + nq.b.nq_nsq = nq.b.nq_ncq = nqueues - 1; + + if (!nvme_set_features(nvme, 0, NVME_FEAT_NQUEUES, nq.r, &nq.r)) { + return (0); + } /* * Always use the same number of submission and completion queues, and @@ -2049,20 +2098,32 @@ nvme_init(nvme_t *nvme) /* * Check for the presence of a Volatile Write Cache. If present, - * enable it by default. + * enable or disable based on the value of the property + * volatile-write-cache-enable (default is enabled). */ - if (nvme->n_idctl->id_vwc.vwc_present == 0) { - nvme->n_volatile_write_cache_enabled = B_FALSE; - nvme_bd_ops.o_sync_cache = NULL; - } else { + nvme->n_write_cache_present = + nvme->n_idctl->id_vwc.vwc_present == 0 ? B_FALSE : B_TRUE; + + (void) ddi_prop_update_int(DDI_DEV_T_NONE, nvme->n_dip, + "volatile-write-cache-present", + nvme->n_write_cache_present ? 1 : 0); + + if (!nvme->n_write_cache_present) { + nvme->n_write_cache_enabled = B_FALSE; + } else if (!nvme_write_cache_set(nvme, nvme->n_write_cache_enabled)) { + dev_err(nvme->n_dip, CE_WARN, + "!failed to %sable volatile write cache", + nvme->n_write_cache_enabled ? "en" : "dis"); /* - * TODO: send SET FEATURES to enable VWC - * (have no hardware to test this) + * Assume the cache is (still) enabled. */ - nvme->n_volatile_write_cache_enabled = B_FALSE; - nvme_bd_ops.o_sync_cache = NULL; + nvme->n_write_cache_enabled = B_TRUE; } + (void) ddi_prop_update_int(DDI_DEV_T_NONE, nvme->n_dip, + "volatile-write-cache-enable", + nvme->n_write_cache_enabled ? 1 : 0); + /* * Grab a copy of all mandatory log pages. * @@ -2427,6 +2488,9 @@ nvme_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) nvme->n_async_event_limit = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "async-event-limit", NVME_DEFAULT_ASYNC_EVENT_LIMIT); + nvme->n_write_cache_enabled = ddi_prop_get_int(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "volatile-write-cache-enable", 1) != 0 ? + B_TRUE : B_FALSE; if (nvme->n_admin_queue_len < NVME_MIN_ADMIN_QUEUE_LEN) nvme->n_admin_queue_len = NVME_MIN_ADMIN_QUEUE_LEN; @@ -2889,14 +2953,19 @@ nvme_bd_sync(void *arg, bd_xfer_t *xfer) return (EIO); /* - * If the volatile write cache isn't enabled the FLUSH command is a - * no-op, so we can take a shortcut here. + * If the volatile write cache is not present or not enabled the FLUSH + * command is a no-op, so we can take a shortcut here. */ - if (ns->ns_nvme->n_volatile_write_cache_enabled == B_FALSE) { + if (!ns->ns_nvme->n_write_cache_present) { bd_xfer_done(xfer, ENOTSUP); return (0); } + if (!ns->ns_nvme->n_write_cache_enabled) { + bd_xfer_done(xfer, 0); + return (0); + } + return (nvme_bd_cmd(ns, xfer, NVME_OPC_NVM_FLUSH)); } diff --git a/usr/src/uts/common/io/nvme/nvme.conf b/usr/src/uts/common/io/nvme/nvme.conf index 186bd38018..97ce46ec6b 100644 --- a/usr/src/uts/common/io/nvme/nvme.conf +++ b/usr/src/uts/common/io/nvme/nvme.conf @@ -8,7 +8,7 @@ # http://www.illumos.org/license/CDDL. # # -# Copyright 2015 Nexenta Systems, Inc. All rights reserved. +# Copyright 2016 Nexenta Systems, Inc. All rights reserved. # # @@ -37,4 +37,7 @@ # overridden here. #async-event-limit=10; - +# +# Enable (1) or Disable (0) the volatile write cache, if present. +# +#volatile-write-cache-enable=1; diff --git a/usr/src/uts/common/io/nvme/nvme_reg.h b/usr/src/uts/common/io/nvme/nvme_reg.h index a4b286c7fa..9c4259fa88 100644 --- a/usr/src/uts/common/io/nvme/nvme_reg.h +++ b/usr/src/uts/common/io/nvme/nvme_reg.h @@ -609,6 +609,15 @@ typedef struct { uint8_t lr_rsvd3[16]; } nvme_lba_range_type_t; +/* Volatile Write Cache Feature */ +typedef union { + struct { + uint32_t wc_wce:1; /* Volatile Write Cache Enable */ + uint32_t wc_rsvd:31; + } b; + uint32_t r; +} nvme_write_cache_t; + /* Number of Queues */ typedef union { struct { diff --git a/usr/src/uts/common/io/nvme/nvme_var.h b/usr/src/uts/common/io/nvme/nvme_var.h index 409ae4841e..488d827eab 100644 --- a/usr/src/uts/common/io/nvme/nvme_var.h +++ b/usr/src/uts/common/io/nvme/nvme_var.h @@ -131,7 +131,8 @@ struct nvme { uint16_t n_async_event_limit; uint16_t n_abort_command_limit; uint64_t n_max_data_transfer_size; - boolean_t n_volatile_write_cache_enabled; + boolean_t n_write_cache_present; + boolean_t n_write_cache_enabled; int n_error_log_len; int n_nssr_supported; |