Print this page
don't block in nvme_bd_cmd
8629 nvme: rework command abortion
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Jason King <jason.king@joyent.com>
Reviewed by: Robert Mustacchi <rm@joyent.com>
*** 54,68 ****
* hold up to 4096 admin commands.
*
* From the hardware perspective both queues of a queue pair are independent,
* but they share some driver state: the command array (holding pointers to
* commands currently being processed by the hardware) and the active command
! * counter. Access to the submission side of a queue pair and the shared state
! * is protected by nq_mutex. The completion side of a queue pair does not need
! * that protection apart from its access to the shared state; it is called only
! * in the interrupt handler which does not run concurrently for the same
! * interrupt vector.
*
* When a command is submitted to a queue pair the active command counter is
* incremented and a pointer to the command is stored in the command array. The
* array index is used as command identifier (CID) in the submission queue
* entry. Some commands may take a very long time to complete, and if the queue
--- 54,65 ----
* hold up to 4096 admin commands.
*
* From the hardware perspective both queues of a queue pair are independent,
* but they share some driver state: the command array (holding pointers to
* commands currently being processed by the hardware) and the active command
! * counter. Access to a queue pair and the shared state is protected by
! * nq_mutex.
*
* When a command is submitted to a queue pair the active command counter is
* incremented and a pointer to the command is stored in the command array. The
* array index is used as command identifier (CID) in the submission queue
* entry. Some commands may take a very long time to complete, and if the queue
*** 147,165 ****
* errors indicating a driver bug the driver panics. Almost all other error
* status values just cause EIO to be returned.
*
* Command timeouts are currently detected for all admin commands except
* asynchronous event requests. If a command times out and the hardware appears
! * to be healthy the driver attempts to abort the command. If this fails the
* driver assumes the device to be dead, fences it off, and calls FMA to retire
! * it. In general admin commands are issued at attach time only. No timeout
! * handling of normal I/O commands is presently done.
*
! * In some cases it may be possible that the ABORT command times out, too. In
! * that case the device is also declared dead and fenced off.
*
*
* Quiesce / Fast Reboot:
*
* The driver currently does not support fast reboot. A quiesce(9E) entry point
* is still provided which is used to send a shutdown notification to the
* device.
--- 144,191 ----
* errors indicating a driver bug the driver panics. Almost all other error
* status values just cause EIO to be returned.
*
* Command timeouts are currently detected for all admin commands except
* asynchronous event requests. If a command times out and the hardware appears
! * to be healthy the driver attempts to abort the command. The original command
! * timeout is also applied to the abort command. If the abort times out too the
* driver assumes the device to be dead, fences it off, and calls FMA to retire
! * it. In all other cases the aborted command should return immediately with a
! * status indicating it was aborted, and the driver will wait indefinitely for
! * that to happen. No timeout handling of normal I/O commands is presently done.
*
! * Any command that times out due to the controller dropping dead will be put on
! * nvme_lost_cmds list if it references DMA memory. This will prevent the DMA
! * memory being reused by the system and later be written to by a "dead" NVMe
! * controller.
*
*
+ * Locking:
+ *
+ * Each queue pair has its own nq_mutex, which must be held when accessing the
+ * associated queue registers or the shared state of the queue pair. Callers of
+ * nvme_unqueue_cmd() must make sure that nq_mutex is held, while
+ * nvme_submit_{admin,io}_cmd() and nvme_retrieve_cmd() take care of this
+ * themselves.
+ *
+ * Each command also has its own nc_mutex, which is associated with the
+ * condition variable nc_cv. It is only used on admin commands which are run
+ * synchronously. In that case it must be held across calls to
+ * nvme_submit_{admin,io}_cmd() and nvme_wait_cmd(), which is taken care of by
+ * nvme_admin_cmd(). It must also be held whenever the completion state of the
+ * command is changed or while a admin command timeout is handled.
+ *
+ * If both nc_mutex and nq_mutex must be held, nc_mutex must be acquired first.
+ * More than one nc_mutex may only be held when aborting commands. In this case,
+ * the nc_mutex of the command to be aborted must be held across the call to
+ * nvme_abort_cmd() to prevent the command from completing while the abort is in
+ * progress.
+ *
+ * Each minor node has its own nm_mutex, which protects the open count nm_ocnt
+ * and exclusive-open flag nm_oexcl.
+ *
+ *
* Quiesce / Fast Reboot:
*
* The driver currently does not support fast reboot. A quiesce(9E) entry point
* is still provided which is used to send a shutdown notification to the
* device.
*** 219,228 ****
--- 245,255 ----
#include <sys/atomic.h>
#include <sys/archsystm.h>
#include <sys/sata/sata_hba.h>
#include <sys/stat.h>
#include <sys/policy.h>
+ #include <sys/list.h>
#include <sys/nvme.h>
#ifdef __x86
#include <sys/x86_archext.h>
*** 255,270 ****
static int nvme_init(nvme_t *);
static nvme_cmd_t *nvme_alloc_cmd(nvme_t *, int);
static void nvme_free_cmd(nvme_cmd_t *);
static nvme_cmd_t *nvme_create_nvm_cmd(nvme_namespace_t *, uint8_t,
bd_xfer_t *);
! static int nvme_admin_cmd(nvme_cmd_t *, int);
static void nvme_submit_admin_cmd(nvme_qpair_t *, nvme_cmd_t *);
static int nvme_submit_io_cmd(nvme_qpair_t *, nvme_cmd_t *);
static void nvme_submit_cmd_common(nvme_qpair_t *, nvme_cmd_t *);
static nvme_cmd_t *nvme_retrieve_cmd(nvme_t *, nvme_qpair_t *);
! static boolean_t nvme_wait_cmd(nvme_cmd_t *, uint_t);
static void nvme_wakeup_cmd(void *);
static void nvme_async_event_task(void *);
static int nvme_check_unknown_cmd_status(nvme_cmd_t *);
static int nvme_check_vendor_cmd_status(nvme_cmd_t *);
--- 282,298 ----
static int nvme_init(nvme_t *);
static nvme_cmd_t *nvme_alloc_cmd(nvme_t *, int);
static void nvme_free_cmd(nvme_cmd_t *);
static nvme_cmd_t *nvme_create_nvm_cmd(nvme_namespace_t *, uint8_t,
bd_xfer_t *);
! static void nvme_admin_cmd(nvme_cmd_t *, int);
static void nvme_submit_admin_cmd(nvme_qpair_t *, nvme_cmd_t *);
static int nvme_submit_io_cmd(nvme_qpair_t *, nvme_cmd_t *);
static void nvme_submit_cmd_common(nvme_qpair_t *, nvme_cmd_t *);
+ static nvme_cmd_t *nvme_unqueue_cmd(nvme_t *, nvme_qpair_t *, int);
static nvme_cmd_t *nvme_retrieve_cmd(nvme_t *, nvme_qpair_t *);
! static void nvme_wait_cmd(nvme_cmd_t *, uint_t);
static void nvme_wakeup_cmd(void *);
static void nvme_async_event_task(void *);
static int nvme_check_unknown_cmd_status(nvme_cmd_t *);
static int nvme_check_vendor_cmd_status(nvme_cmd_t *);
*** 271,292 ****
static int nvme_check_integrity_cmd_status(nvme_cmd_t *);
static int nvme_check_specific_cmd_status(nvme_cmd_t *);
static int nvme_check_generic_cmd_status(nvme_cmd_t *);
static inline int nvme_check_cmd_status(nvme_cmd_t *);
! static void nvme_abort_cmd(nvme_cmd_t *);
static void nvme_async_event(nvme_t *);
static int nvme_format_nvm(nvme_t *, uint32_t, uint8_t, boolean_t, uint8_t,
boolean_t, uint8_t);
static int nvme_get_logpage(nvme_t *, void **, size_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_get_features(nvme_t *, uint32_t, uint8_t, uint32_t *,
void **, size_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 *);
static int nvme_zalloc_dma(nvme_t *, size_t, uint_t, ddi_dma_attr_t *,
nvme_dma_t **);
static int nvme_zalloc_queue_dma(nvme_t *, uint32_t, uint16_t, uint_t,
--- 299,320 ----
static int nvme_check_integrity_cmd_status(nvme_cmd_t *);
static int nvme_check_specific_cmd_status(nvme_cmd_t *);
static int nvme_check_generic_cmd_status(nvme_cmd_t *);
static inline int nvme_check_cmd_status(nvme_cmd_t *);
! static int nvme_abort_cmd(nvme_cmd_t *, uint_t);
static void nvme_async_event(nvme_t *);
static int nvme_format_nvm(nvme_t *, uint32_t, uint8_t, boolean_t, uint8_t,
boolean_t, uint8_t);
static int nvme_get_logpage(nvme_t *, void **, size_t *, uint8_t, ...);
! static int nvme_identify(nvme_t *, uint32_t, void **);
! static int nvme_set_features(nvme_t *, uint32_t, uint8_t, uint32_t,
uint32_t *);
! static int nvme_get_features(nvme_t *, uint32_t, uint8_t, uint32_t *,
void **, size_t *);
! static int 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 *);
static int nvme_zalloc_dma(nvme_t *, size_t, uint_t, ddi_dma_attr_t *,
nvme_dma_t **);
static int nvme_zalloc_queue_dma(nvme_t *, uint32_t, uint16_t, uint_t,
*** 459,468 ****
--- 487,505 ----
.o_sync_cache = nvme_bd_sync,
.o_read = nvme_bd_read,
.o_write = nvme_bd_write,
};
+ /*
+ * This list will hold commands that have timed out and couldn't be aborted.
+ * As we don't know what the hardware may still do with the DMA memory we can't
+ * free them, so we'll keep them forever on this list where we can easily look
+ * at them with mdb.
+ */
+ static struct list nvme_lost_cmds;
+ static kmutex_t nvme_lc_mutex;
+
int
_init(void)
{
int error;
*** 471,485 ****
--- 508,528 ----
return (error);
nvme_cmd_cache = kmem_cache_create("nvme_cmd_cache",
sizeof (nvme_cmd_t), 64, NULL, NULL, NULL, NULL, NULL, 0);
+ mutex_init(&nvme_lc_mutex, NULL, MUTEX_DRIVER, NULL);
+ list_create(&nvme_lost_cmds, sizeof (nvme_cmd_t),
+ offsetof(nvme_cmd_t, nc_list));
+
bd_mod_init(&nvme_dev_ops);
error = mod_install(&nvme_modlinkage);
if (error != DDI_SUCCESS) {
ddi_soft_state_fini(&nvme_state);
+ mutex_destroy(&nvme_lc_mutex);
+ list_destroy(&nvme_lost_cmds);
bd_mod_fini(&nvme_dev_ops);
}
return (error);
}
*** 487,500 ****
--- 530,548 ----
int
_fini(void)
{
int error;
+ if (!list_is_empty(&nvme_lost_cmds))
+ return (DDI_FAILURE);
+
error = mod_remove(&nvme_modlinkage);
if (error == DDI_SUCCESS) {
ddi_soft_state_fini(&nvme_state);
kmem_cache_destroy(nvme_cmd_cache);
+ mutex_destroy(&nvme_lc_mutex);
+ list_destroy(&nvme_lost_cmds);
bd_mod_fini(&nvme_dev_ops);
}
return (error);
}
*** 800,809 ****
--- 848,861 ----
}
static void
nvme_free_cmd(nvme_cmd_t *cmd)
{
+ /* Don't free commands on the lost commands list. */
+ if (list_link_active(&cmd->nc_list))
+ return;
+
if (cmd->nc_dma) {
if (cmd->nc_dma->nd_cached)
kmem_cache_free(cmd->nc_nvme->n_prp_cache,
cmd->nc_dma);
else
*** 866,875 ****
--- 918,948 ----
mutex_exit(&qp->nq_mutex);
}
static nvme_cmd_t *
+ nvme_unqueue_cmd(nvme_t *nvme, nvme_qpair_t *qp, int cid)
+ {
+ nvme_cmd_t *cmd;
+
+ ASSERT(mutex_owned(&qp->nq_mutex));
+ ASSERT3S(cid, <, qp->nq_nentry);
+
+ cmd = qp->nq_cmd[cid];
+ qp->nq_cmd[cid] = NULL;
+ ASSERT3U(qp->nq_active_cmds, >, 0);
+ qp->nq_active_cmds--;
+ sema_v(&qp->nq_sema);
+
+ ASSERT3P(cmd, !=, NULL);
+ ASSERT3P(cmd->nc_nvme, ==, nvme);
+ ASSERT3S(cmd->nc_sqe.sqe_cid, ==, cid);
+
+ return (cmd);
+ }
+
+ static nvme_cmd_t *
nvme_retrieve_cmd(nvme_t *nvme, nvme_qpair_t *qp)
{
nvme_reg_cqhdbl_t head = { 0 };
nvme_cqe_t *cqe;
*** 886,905 ****
mutex_exit(&qp->nq_mutex);
return (NULL);
}
ASSERT(nvme->n_ioq[cqe->cqe_sqid] == qp);
- ASSERT(cqe->cqe_cid < qp->nq_nentry);
! cmd = qp->nq_cmd[cqe->cqe_cid];
! qp->nq_cmd[cqe->cqe_cid] = NULL;
! qp->nq_active_cmds--;
- ASSERT(cmd != NULL);
- ASSERT(cmd->nc_nvme == nvme);
ASSERT(cmd->nc_sqid == cqe->cqe_sqid);
- ASSERT(cmd->nc_sqe.sqe_cid == cqe->cqe_cid);
bcopy(cqe, &cmd->nc_cqe, sizeof (nvme_cqe_t));
qp->nq_sqhead = cqe->cqe_sqhd;
head.b.cqhdbl_cqh = qp->nq_cqhead = (qp->nq_cqhead + 1) % qp->nq_nentry;
--- 959,972 ----
mutex_exit(&qp->nq_mutex);
return (NULL);
}
ASSERT(nvme->n_ioq[cqe->cqe_sqid] == qp);
! cmd = nvme_unqueue_cmd(nvme, qp, cqe->cqe_cid);
ASSERT(cmd->nc_sqid == cqe->cqe_sqid);
bcopy(cqe, &cmd->nc_cqe, sizeof (nvme_cqe_t));
qp->nq_sqhead = cqe->cqe_sqhd;
head.b.cqhdbl_cqh = qp->nq_cqhead = (qp->nq_cqhead + 1) % qp->nq_nentry;
*** 908,918 ****
if (qp->nq_cqhead == 0)
qp->nq_phase = qp->nq_phase ? 0 : 1;
nvme_put32(cmd->nc_nvme, qp->nq_cqhdbl, head.r);
mutex_exit(&qp->nq_mutex);
- sema_v(&qp->nq_sema);
return (cmd);
}
static int
--- 975,984 ----
*** 1193,1203 ****
static inline int
nvme_check_cmd_status(nvme_cmd_t *cmd)
{
nvme_cqe_t *cqe = &cmd->nc_cqe;
! /* take a shortcut if everything is alright */
if (cqe->cqe_sf.sf_sct == NVME_CQE_SCT_GENERIC &&
cqe->cqe_sf.sf_sc == NVME_CQE_SC_GEN_SUCCESS)
return (0);
if (cqe->cqe_sf.sf_sct == NVME_CQE_SCT_GENERIC)
--- 1259,1275 ----
static inline int
nvme_check_cmd_status(nvme_cmd_t *cmd)
{
nvme_cqe_t *cqe = &cmd->nc_cqe;
! /*
! * Take a shortcut if the controller is dead, or if
! * command status indicates no error.
! */
! if (cmd->nc_nvme->n_dead)
! return (EIO);
!
if (cqe->cqe_sf.sf_sct == NVME_CQE_SCT_GENERIC &&
cqe->cqe_sf.sf_sc == NVME_CQE_SC_GEN_SUCCESS)
return (0);
if (cqe->cqe_sf.sf_sct == NVME_CQE_SCT_GENERIC)
*** 1210,1372 ****
return (nvme_check_vendor_cmd_status(cmd));
return (nvme_check_unknown_cmd_status(cmd));
}
! /*
! * nvme_abort_cmd_cb -- replaces nc_callback of aborted commands
! *
! * This functions takes care of cleaning up aborted commands. The command
! * status is checked to catch any fatal errors.
! */
! static void
! nvme_abort_cmd_cb(void *arg)
{
- nvme_cmd_t *cmd = arg;
-
- /*
- * Grab the command mutex. Once we have it we hold the last reference
- * to the command and can safely free it.
- */
- mutex_enter(&cmd->nc_mutex);
- (void) nvme_check_cmd_status(cmd);
- mutex_exit(&cmd->nc_mutex);
-
- nvme_free_cmd(cmd);
- }
-
- static void
- nvme_abort_cmd(nvme_cmd_t *abort_cmd)
- {
nvme_t *nvme = abort_cmd->nc_nvme;
nvme_cmd_t *cmd = nvme_alloc_cmd(nvme, KM_SLEEP);
nvme_abort_cmd_t ac = { 0 };
sema_p(&nvme->n_abort_sema);
ac.b.ac_cid = abort_cmd->nc_sqe.sqe_cid;
ac.b.ac_sqid = abort_cmd->nc_sqid;
- /*
- * Drop the mutex of the aborted command. From this point on
- * we must assume that the abort callback has freed the command.
- */
- mutex_exit(&abort_cmd->nc_mutex);
-
cmd->nc_sqid = 0;
cmd->nc_sqe.sqe_opc = NVME_OPC_ABORT;
cmd->nc_callback = nvme_wakeup_cmd;
cmd->nc_sqe.sqe_cdw10 = ac.r;
/*
* Send the ABORT to the hardware. The ABORT command will return _after_
! * the aborted command has completed (aborted or otherwise).
*/
! if (nvme_admin_cmd(cmd, nvme_admin_cmd_timeout) != DDI_SUCCESS) {
sema_v(&nvme->n_abort_sema);
- dev_err(nvme->n_dip, CE_WARN,
- "!nvme_admin_cmd failed for ABORT");
- atomic_inc_32(&nvme->n_abort_failed);
- return;
- }
- sema_v(&nvme->n_abort_sema);
! if (nvme_check_cmd_status(cmd)) {
dev_err(nvme->n_dip, CE_WARN,
"!ABORT failed with sct = %x, sc = %x",
cmd->nc_cqe.cqe_sf.sf_sct, cmd->nc_cqe.cqe_sf.sf_sc);
atomic_inc_32(&nvme->n_abort_failed);
} else {
atomic_inc_32(&nvme->n_cmd_aborted);
}
nvme_free_cmd(cmd);
}
/*
* nvme_wait_cmd -- wait for command completion or timeout
*
- * Returns B_TRUE if the command completed normally.
- *
- * Returns B_FALSE if the command timed out and an abort was attempted. The
- * command mutex will be dropped and the command must be considered freed. The
- * freeing of the command is normally done by the abort command callback.
- *
* In case of a serious error or a timeout of the abort command the hardware
* will be declared dead and FMA will be notified.
*/
! static boolean_t
nvme_wait_cmd(nvme_cmd_t *cmd, uint_t sec)
{
clock_t timeout = ddi_get_lbolt() + drv_usectohz(sec * MICROSEC);
nvme_t *nvme = cmd->nc_nvme;
nvme_reg_csts_t csts;
ASSERT(mutex_owned(&cmd->nc_mutex));
while (!cmd->nc_completed) {
if (cv_timedwait(&cmd->nc_cv, &cmd->nc_mutex, timeout) == -1)
break;
}
if (cmd->nc_completed)
! return (B_TRUE);
/*
! * The command timed out. Change the callback to the cleanup function.
! */
! cmd->nc_callback = nvme_abort_cmd_cb;
!
! /*
* Check controller for fatal status, any errors associated with the
* register or DMA handle, or for a double timeout (abort command timed
* out). If necessary log a warning and call FMA.
*/
csts.r = nvme_get32(nvme, NVME_REG_CSTS);
! dev_err(nvme->n_dip, CE_WARN, "!command timeout, "
! "OPC = %x, CFS = %d", cmd->nc_sqe.sqe_opc, csts.b.csts_cfs);
atomic_inc_32(&nvme->n_cmd_timeout);
if (csts.b.csts_cfs ||
nvme_check_regs_hdl(nvme) ||
nvme_check_dma_hdl(cmd->nc_dma) ||
cmd->nc_sqe.sqe_opc == NVME_OPC_ABORT) {
ddi_fm_service_impact(nvme->n_dip, DDI_SERVICE_LOST);
nvme->n_dead = B_TRUE;
! mutex_exit(&cmd->nc_mutex);
! } else {
/*
! * Try to abort the command. The command mutex is released by
! * nvme_abort_cmd().
! * If the abort succeeds it will have freed the aborted command.
! * If the abort fails for other reasons we must assume that the
! * command may complete at any time, and the callback will free
! * it for us.
*/
! nvme_abort_cmd(cmd);
}
! return (B_FALSE);
}
static void
nvme_wakeup_cmd(void *arg)
{
nvme_cmd_t *cmd = arg;
mutex_enter(&cmd->nc_mutex);
- /*
- * There is a slight chance that this command completed shortly after
- * the timeout was hit in nvme_wait_cmd() but before the callback was
- * changed. Catch that case here and clean up accordingly.
- */
- if (cmd->nc_callback == nvme_abort_cmd_cb) {
- mutex_exit(&cmd->nc_mutex);
- nvme_abort_cmd_cb(cmd);
- return;
- }
-
cmd->nc_completed = B_TRUE;
cv_signal(&cmd->nc_cv);
mutex_exit(&cmd->nc_mutex);
}
--- 1282,1414 ----
return (nvme_check_vendor_cmd_status(cmd));
return (nvme_check_unknown_cmd_status(cmd));
}
! static int
! nvme_abort_cmd(nvme_cmd_t *abort_cmd, uint_t sec)
{
nvme_t *nvme = abort_cmd->nc_nvme;
nvme_cmd_t *cmd = nvme_alloc_cmd(nvme, KM_SLEEP);
nvme_abort_cmd_t ac = { 0 };
+ int ret = 0;
sema_p(&nvme->n_abort_sema);
ac.b.ac_cid = abort_cmd->nc_sqe.sqe_cid;
ac.b.ac_sqid = abort_cmd->nc_sqid;
cmd->nc_sqid = 0;
cmd->nc_sqe.sqe_opc = NVME_OPC_ABORT;
cmd->nc_callback = nvme_wakeup_cmd;
cmd->nc_sqe.sqe_cdw10 = ac.r;
/*
* Send the ABORT to the hardware. The ABORT command will return _after_
! * the aborted command has completed (aborted or otherwise), but since
! * we still hold the aborted command's mutex its callback hasn't been
! * processed yet.
*/
! nvme_admin_cmd(cmd, sec);
sema_v(&nvme->n_abort_sema);
! if ((ret = nvme_check_cmd_status(cmd)) != 0) {
dev_err(nvme->n_dip, CE_WARN,
"!ABORT failed with sct = %x, sc = %x",
cmd->nc_cqe.cqe_sf.sf_sct, cmd->nc_cqe.cqe_sf.sf_sc);
atomic_inc_32(&nvme->n_abort_failed);
} else {
+ dev_err(nvme->n_dip, CE_WARN,
+ "!ABORT of command %d/%d %ssuccessful",
+ abort_cmd->nc_sqe.sqe_cid, abort_cmd->nc_sqid,
+ cmd->nc_cqe.cqe_dw0 & 1 ? "un" : "");
+ if ((cmd->nc_cqe.cqe_dw0 & 1) == 0)
atomic_inc_32(&nvme->n_cmd_aborted);
}
nvme_free_cmd(cmd);
+ return (ret);
}
/*
* nvme_wait_cmd -- wait for command completion or timeout
*
* In case of a serious error or a timeout of the abort command the hardware
* will be declared dead and FMA will be notified.
*/
! static void
nvme_wait_cmd(nvme_cmd_t *cmd, uint_t sec)
{
clock_t timeout = ddi_get_lbolt() + drv_usectohz(sec * MICROSEC);
nvme_t *nvme = cmd->nc_nvme;
nvme_reg_csts_t csts;
+ nvme_qpair_t *qp;
ASSERT(mutex_owned(&cmd->nc_mutex));
while (!cmd->nc_completed) {
if (cv_timedwait(&cmd->nc_cv, &cmd->nc_mutex, timeout) == -1)
break;
}
if (cmd->nc_completed)
! return;
/*
! * The command timed out.
! *
* Check controller for fatal status, any errors associated with the
* register or DMA handle, or for a double timeout (abort command timed
* out). If necessary log a warning and call FMA.
*/
csts.r = nvme_get32(nvme, NVME_REG_CSTS);
! dev_err(nvme->n_dip, CE_WARN, "!command %d/%d timeout, "
! "OPC = %x, CFS = %d", cmd->nc_sqe.sqe_cid, cmd->nc_sqid,
! cmd->nc_sqe.sqe_opc, csts.b.csts_cfs);
atomic_inc_32(&nvme->n_cmd_timeout);
if (csts.b.csts_cfs ||
nvme_check_regs_hdl(nvme) ||
nvme_check_dma_hdl(cmd->nc_dma) ||
cmd->nc_sqe.sqe_opc == NVME_OPC_ABORT) {
ddi_fm_service_impact(nvme->n_dip, DDI_SERVICE_LOST);
nvme->n_dead = B_TRUE;
! } else if (nvme_abort_cmd(cmd, sec) == 0) {
/*
! * If the abort succeeded the command should complete
! * immediately with an appropriate status.
*/
! while (!cmd->nc_completed)
! cv_wait(&cmd->nc_cv, &cmd->nc_mutex);
!
! return;
}
! qp = nvme->n_ioq[cmd->nc_sqid];
!
! mutex_enter(&qp->nq_mutex);
! (void) nvme_unqueue_cmd(nvme, qp, cmd->nc_sqe.sqe_cid);
! mutex_exit(&qp->nq_mutex);
!
! /*
! * As we don't know what the presumed dead hardware might still do with
! * the DMA memory, we'll put the command on the lost commands list if it
! * has any DMA memory.
! */
! if (cmd->nc_dma != NULL) {
! mutex_enter(&nvme_lc_mutex);
! list_insert_head(&nvme_lost_cmds, cmd);
! mutex_exit(&nvme_lc_mutex);
! }
}
static void
nvme_wakeup_cmd(void *arg)
{
nvme_cmd_t *cmd = arg;
mutex_enter(&cmd->nc_mutex);
cmd->nc_completed = B_TRUE;
cv_signal(&cmd->nc_cv);
mutex_exit(&cmd->nc_mutex);
}
*** 1388,1398 ****
*
* Other possible errors are various scenarios where the async request
* was aborted, or internal errors in the device. Internal errors are
* reported to FMA, the command aborts need no special handling here.
*/
! if (nvme_check_cmd_status(cmd)) {
dev_err(cmd->nc_nvme->n_dip, CE_WARN,
"!async event request returned failure, sct = %x, "
"sc = %x, dnr = %d, m = %d", cmd->nc_cqe.cqe_sf.sf_sct,
cmd->nc_cqe.cqe_sf.sf_sc, cmd->nc_cqe.cqe_sf.sf_dnr,
cmd->nc_cqe.cqe_sf.sf_m);
--- 1430,1440 ----
*
* Other possible errors are various scenarios where the async request
* was aborted, or internal errors in the device. Internal errors are
* reported to FMA, the command aborts need no special handling here.
*/
! if (nvme_check_cmd_status(cmd) != 0) {
dev_err(cmd->nc_nvme->n_dip, CE_WARN,
"!async event request returned failure, sct = %x, "
"sc = %x, dnr = %d, m = %d", cmd->nc_cqe.cqe_sf.sf_sct,
cmd->nc_cqe.cqe_sf.sf_sc, cmd->nc_cqe.cqe_sf.sf_dnr,
cmd->nc_cqe.cqe_sf.sf_m);
*** 1520,1545 ****
if (health_log)
kmem_free(health_log, logsize);
}
! static int
nvme_admin_cmd(nvme_cmd_t *cmd, int sec)
{
mutex_enter(&cmd->nc_mutex);
nvme_submit_admin_cmd(cmd->nc_nvme->n_adminq, cmd);
!
! if (nvme_wait_cmd(cmd, sec) == B_FALSE) {
! /*
! * The command timed out. An abort command was posted that
! * will take care of the cleanup.
! */
! return (DDI_FAILURE);
! }
mutex_exit(&cmd->nc_mutex);
-
- return (DDI_SUCCESS);
}
static void
nvme_async_event(nvme_t *nvme)
{
--- 1562,1578 ----
if (health_log)
kmem_free(health_log, logsize);
}
! static void
nvme_admin_cmd(nvme_cmd_t *cmd, int sec)
{
mutex_enter(&cmd->nc_mutex);
nvme_submit_admin_cmd(cmd->nc_nvme->n_adminq, cmd);
! nvme_wait_cmd(cmd, sec);
mutex_exit(&cmd->nc_mutex);
}
static void
nvme_async_event(nvme_t *nvme)
{
*** 1577,1592 ****
* namespaces in one command. Handle that gracefully.
*/
if (nsid == (uint32_t)-1)
cmd->nc_dontpanic = B_TRUE;
! if ((ret = nvme_admin_cmd(cmd, nvme_format_cmd_timeout))
! != DDI_SUCCESS) {
! dev_err(nvme->n_dip, CE_WARN,
! "!nvme_admin_cmd failed for FORMAT NVM");
! return (EIO);
! }
if ((ret = nvme_check_cmd_status(cmd)) != 0) {
dev_err(nvme->n_dip, CE_WARN,
"!FORMAT failed with sct = %x, sc = %x",
cmd->nc_cqe.cqe_sf.sf_sct, cmd->nc_cqe.cqe_sf.sf_sc);
--- 1610,1620 ----
* namespaces in one command. Handle that gracefully.
*/
if (nsid == (uint32_t)-1)
cmd->nc_dontpanic = B_TRUE;
! nvme_admin_cmd(cmd, nvme_format_cmd_timeout);
if ((ret = nvme_check_cmd_status(cmd)) != 0) {
dev_err(nvme->n_dip, CE_WARN,
"!FORMAT failed with sct = %x, sc = %x",
cmd->nc_cqe.cqe_sf.sf_sct, cmd->nc_cqe.cqe_sf.sf_sc);
*** 1601,1611 ****
...)
{
nvme_cmd_t *cmd = nvme_alloc_cmd(nvme, KM_SLEEP);
nvme_getlogpage_t getlogpage = { 0 };
va_list ap;
! int ret = DDI_FAILURE;
va_start(ap, logpage);
cmd->nc_sqid = 0;
cmd->nc_callback = nvme_wakeup_cmd;
--- 1629,1639 ----
...)
{
nvme_cmd_t *cmd = nvme_alloc_cmd(nvme, KM_SLEEP);
nvme_getlogpage_t getlogpage = { 0 };
va_list ap;
! int ret;
va_start(ap, logpage);
cmd->nc_sqid = 0;
cmd->nc_callback = nvme_wakeup_cmd;
*** 1636,1645 ****
--- 1664,1674 ----
default:
dev_err(nvme->n_dip, CE_WARN, "!unknown log page requested: %d",
logpage);
atomic_inc_32(&nvme->n_unknown_logpage);
+ ret = EINVAL;
goto fail;
}
va_end(ap);
*** 1649,1665 ****
--- 1678,1696 ----
if (nvme_zalloc_dma(nvme, getlogpage.b.lp_numd * sizeof (uint32_t),
DDI_DMA_READ, &nvme->n_prp_dma_attr, &cmd->nc_dma) != DDI_SUCCESS) {
dev_err(nvme->n_dip, CE_WARN,
"!nvme_zalloc_dma failed for GET LOG PAGE");
+ ret = ENOMEM;
goto fail;
}
if (cmd->nc_dma->nd_ncookie > 2) {
dev_err(nvme->n_dip, CE_WARN,
"!too many DMA cookies for GET LOG PAGE");
atomic_inc_32(&nvme->n_too_many_cookies);
+ ret = ENOMEM;
goto fail;
}
cmd->nc_sqe.sqe_dptr.d_prp[0] = cmd->nc_dma->nd_cookie.dmac_laddress;
if (cmd->nc_dma->nd_ncookie > 1) {
*** 1667,1706 ****
&cmd->nc_dma->nd_cookie);
cmd->nc_sqe.sqe_dptr.d_prp[1] =
cmd->nc_dma->nd_cookie.dmac_laddress;
}
! if (nvme_admin_cmd(cmd, nvme_admin_cmd_timeout) != DDI_SUCCESS) {
! dev_err(nvme->n_dip, CE_WARN,
! "!nvme_admin_cmd failed for GET LOG PAGE");
! return (ret);
! }
! if (nvme_check_cmd_status(cmd)) {
dev_err(nvme->n_dip, CE_WARN,
"!GET LOG PAGE failed with sct = %x, sc = %x",
cmd->nc_cqe.cqe_sf.sf_sct, cmd->nc_cqe.cqe_sf.sf_sc);
goto fail;
}
*buf = kmem_alloc(*bufsize, KM_SLEEP);
bcopy(cmd->nc_dma->nd_memp, *buf, *bufsize);
- ret = DDI_SUCCESS;
-
fail:
nvme_free_cmd(cmd);
return (ret);
}
! static void *
! nvme_identify(nvme_t *nvme, uint32_t nsid)
{
nvme_cmd_t *cmd = nvme_alloc_cmd(nvme, KM_SLEEP);
! void *buf = NULL;
cmd->nc_sqid = 0;
cmd->nc_callback = nvme_wakeup_cmd;
cmd->nc_sqe.sqe_opc = NVME_OPC_IDENTIFY;
cmd->nc_sqe.sqe_nsid = nsid;
cmd->nc_sqe.sqe_cdw10 = nsid ? NVME_IDENTIFY_NSID : NVME_IDENTIFY_CTRL;
--- 1698,1734 ----
&cmd->nc_dma->nd_cookie);
cmd->nc_sqe.sqe_dptr.d_prp[1] =
cmd->nc_dma->nd_cookie.dmac_laddress;
}
! nvme_admin_cmd(cmd, nvme_admin_cmd_timeout);
! if ((ret = nvme_check_cmd_status(cmd)) != 0) {
dev_err(nvme->n_dip, CE_WARN,
"!GET LOG PAGE failed with sct = %x, sc = %x",
cmd->nc_cqe.cqe_sf.sf_sct, cmd->nc_cqe.cqe_sf.sf_sc);
goto fail;
}
*buf = kmem_alloc(*bufsize, KM_SLEEP);
bcopy(cmd->nc_dma->nd_memp, *buf, *bufsize);
fail:
nvme_free_cmd(cmd);
return (ret);
}
! static int
! nvme_identify(nvme_t *nvme, uint32_t nsid, void **buf)
{
nvme_cmd_t *cmd = nvme_alloc_cmd(nvme, KM_SLEEP);
! int ret;
+ if (buf == NULL)
+ return (EINVAL);
+
cmd->nc_sqid = 0;
cmd->nc_callback = nvme_wakeup_cmd;
cmd->nc_sqe.sqe_opc = NVME_OPC_IDENTIFY;
cmd->nc_sqe.sqe_nsid = nsid;
cmd->nc_sqe.sqe_cdw10 = nsid ? NVME_IDENTIFY_NSID : NVME_IDENTIFY_CTRL;
*** 1707,1723 ****
--- 1735,1753 ----
if (nvme_zalloc_dma(nvme, NVME_IDENTIFY_BUFSIZE, DDI_DMA_READ,
&nvme->n_prp_dma_attr, &cmd->nc_dma) != DDI_SUCCESS) {
dev_err(nvme->n_dip, CE_WARN,
"!nvme_zalloc_dma failed for IDENTIFY");
+ ret = ENOMEM;
goto fail;
}
if (cmd->nc_dma->nd_ncookie > 2) {
dev_err(nvme->n_dip, CE_WARN,
"!too many DMA cookies for IDENTIFY");
atomic_inc_32(&nvme->n_too_many_cookies);
+ ret = ENOMEM;
goto fail;
}
cmd->nc_sqe.sqe_dptr.d_prp[0] = cmd->nc_dma->nd_cookie.dmac_laddress;
if (cmd->nc_dma->nd_ncookie > 1) {
*** 1725,1763 ****
&cmd->nc_dma->nd_cookie);
cmd->nc_sqe.sqe_dptr.d_prp[1] =
cmd->nc_dma->nd_cookie.dmac_laddress;
}
! if (nvme_admin_cmd(cmd, nvme_admin_cmd_timeout) != DDI_SUCCESS) {
! dev_err(nvme->n_dip, CE_WARN,
! "!nvme_admin_cmd failed for IDENTIFY");
! return (NULL);
! }
! if (nvme_check_cmd_status(cmd)) {
dev_err(nvme->n_dip, CE_WARN,
"!IDENTIFY failed with sct = %x, sc = %x",
cmd->nc_cqe.cqe_sf.sf_sct, cmd->nc_cqe.cqe_sf.sf_sc);
goto fail;
}
! buf = kmem_alloc(NVME_IDENTIFY_BUFSIZE, KM_SLEEP);
! bcopy(cmd->nc_dma->nd_memp, buf, NVME_IDENTIFY_BUFSIZE);
fail:
nvme_free_cmd(cmd);
! return (buf);
}
! 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);
! boolean_t ret = B_FALSE;
ASSERT(res != NULL);
cmd->nc_sqid = 0;
cmd->nc_callback = nvme_wakeup_cmd;
--- 1755,1789 ----
&cmd->nc_dma->nd_cookie);
cmd->nc_sqe.sqe_dptr.d_prp[1] =
cmd->nc_dma->nd_cookie.dmac_laddress;
}
! nvme_admin_cmd(cmd, nvme_admin_cmd_timeout);
! if ((ret = nvme_check_cmd_status(cmd)) != 0) {
dev_err(nvme->n_dip, CE_WARN,
"!IDENTIFY failed with sct = %x, sc = %x",
cmd->nc_cqe.cqe_sf.sf_sct, cmd->nc_cqe.cqe_sf.sf_sc);
goto fail;
}
! *buf = kmem_alloc(NVME_IDENTIFY_BUFSIZE, KM_SLEEP);
! bcopy(cmd->nc_dma->nd_memp, *buf, NVME_IDENTIFY_BUFSIZE);
fail:
nvme_free_cmd(cmd);
! return (ret);
}
! static int
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);
! int ret = EINVAL;
ASSERT(res != NULL);
cmd->nc_sqid = 0;
cmd->nc_callback = nvme_wakeup_cmd;
*** 1776,1813 ****
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");
! return (ret);
! }
! if (nvme_check_cmd_status(cmd)) {
dev_err(nvme->n_dip, CE_WARN,
"!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;
}
*res = cmd->nc_cqe.cqe_dw0;
- ret = B_TRUE;
fail:
nvme_free_cmd(cmd);
return (ret);
}
! static boolean_t
nvme_get_features(nvme_t *nvme, uint32_t nsid, uint8_t feature, uint32_t *res,
void **buf, size_t *bufsize)
{
nvme_cmd_t *cmd = nvme_alloc_cmd(nvme, KM_SLEEP);
! boolean_t ret = B_FALSE;
ASSERT(res != NULL);
if (bufsize != NULL)
*bufsize = 0;
--- 1802,1834 ----
default:
goto fail;
}
! nvme_admin_cmd(cmd, nvme_admin_cmd_timeout);
! if ((ret = nvme_check_cmd_status(cmd)) != 0) {
dev_err(nvme->n_dip, CE_WARN,
"!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;
}
*res = cmd->nc_cqe.cqe_dw0;
fail:
nvme_free_cmd(cmd);
return (ret);
}
! static int
nvme_get_features(nvme_t *nvme, uint32_t nsid, uint8_t feature, uint32_t *res,
void **buf, size_t *bufsize)
{
nvme_cmd_t *cmd = nvme_alloc_cmd(nvme, KM_SLEEP);
! int ret = EINVAL;
ASSERT(res != NULL);
if (bufsize != NULL)
*bufsize = 0;
*** 1869,1885 ****
--- 1890,1908 ----
if (bufsize != NULL && *bufsize != 0) {
if (nvme_zalloc_dma(nvme, *bufsize, DDI_DMA_READ,
&nvme->n_prp_dma_attr, &cmd->nc_dma) != DDI_SUCCESS) {
dev_err(nvme->n_dip, CE_WARN,
"!nvme_zalloc_dma failed for GET FEATURES");
+ ret = ENOMEM;
goto fail;
}
if (cmd->nc_dma->nd_ncookie > 2) {
dev_err(nvme->n_dip, CE_WARN,
"!too many DMA cookies for GET FEATURES");
atomic_inc_32(&nvme->n_too_many_cookies);
+ ret = ENOMEM;
goto fail;
}
cmd->nc_sqe.sqe_dptr.d_prp[0] =
cmd->nc_dma->nd_cookie.dmac_laddress;
*** 1889,1905 ****
cmd->nc_sqe.sqe_dptr.d_prp[1] =
cmd->nc_dma->nd_cookie.dmac_laddress;
}
}
! if (nvme_admin_cmd(cmd, nvme_admin_cmd_timeout) != DDI_SUCCESS) {
! dev_err(nvme->n_dip, CE_WARN,
! "!nvme_admin_cmd failed for GET FEATURES");
! return (ret);
! }
! if (nvme_check_cmd_status(cmd)) {
if (feature == NVME_FEAT_LBA_RANGE &&
cmd->nc_cqe.cqe_sf.sf_sct == NVME_CQE_SCT_GENERIC &&
cmd->nc_cqe.cqe_sf.sf_sc == NVME_CQE_SC_GEN_INV_FLD)
nvme->n_lba_range_supported = B_FALSE;
else
--- 1912,1924 ----
cmd->nc_sqe.sqe_dptr.d_prp[1] =
cmd->nc_dma->nd_cookie.dmac_laddress;
}
}
! nvme_admin_cmd(cmd, nvme_admin_cmd_timeout);
! if ((ret = nvme_check_cmd_status(cmd)) != 0) {
if (feature == NVME_FEAT_LBA_RANGE &&
cmd->nc_cqe.cqe_sf.sf_sct == NVME_CQE_SCT_GENERIC &&
cmd->nc_cqe.cqe_sf.sf_sc == NVME_CQE_SC_GEN_INV_FLD)
nvme->n_lba_range_supported = B_FALSE;
else
*** 1915,1970 ****
*buf = kmem_alloc(*bufsize, KM_SLEEP);
bcopy(cmd->nc_dma->nd_memp, *buf, *bufsize);
}
*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_nqueues_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
! * never use more than the requested number of queues.
*/
! return (MIN(nqueues, MIN(nq.b.nq_nsq, nq.b.nq_ncq) + 1));
}
static int
nvme_create_io_qpair(nvme_t *nvme, nvme_qpair_t *qp, uint16_t idx)
{
nvme_cmd_t *cmd = nvme_alloc_cmd(nvme, KM_SLEEP);
nvme_create_queue_dw10_t dw10 = { 0 };
nvme_create_cq_dw11_t c_dw11 = { 0 };
nvme_create_sq_dw11_t s_dw11 = { 0 };
dw10.b.q_qid = idx;
dw10.b.q_qsize = qp->nq_nentry - 1;
c_dw11.b.cq_pc = 1;
--- 1934,1991 ----
*buf = kmem_alloc(*bufsize, KM_SLEEP);
bcopy(cmd->nc_dma->nd_memp, *buf, *bufsize);
}
*res = cmd->nc_cqe.cqe_dw0;
fail:
nvme_free_cmd(cmd);
return (ret);
}
! static int
nvme_write_cache_set(nvme_t *nvme, boolean_t enable)
{
nvme_write_cache_t nwc = { 0 };
if (enable)
nwc.b.wc_wce = 1;
! return (nvme_set_features(nvme, 0, NVME_FEAT_WRITE_CACHE, nwc.r,
! &nwc.r));
}
static int
! nvme_set_nqueues(nvme_t *nvme, uint16_t *nqueues)
{
nvme_nqueues_t nq = { 0 };
+ int ret;
! nq.b.nq_nsq = nq.b.nq_ncq = *nqueues - 1;
! ret = nvme_set_features(nvme, 0, NVME_FEAT_NQUEUES, nq.r, &nq.r);
+ if (ret == 0) {
/*
! * Always use the same number of submission and completion
! * queues, and never use more than the requested number of
! * queues.
*/
! *nqueues = MIN(*nqueues, MIN(nq.b.nq_nsq, nq.b.nq_ncq) + 1);
! }
!
! return (ret);
}
static int
nvme_create_io_qpair(nvme_t *nvme, nvme_qpair_t *qp, uint16_t idx)
{
nvme_cmd_t *cmd = nvme_alloc_cmd(nvme, KM_SLEEP);
nvme_create_queue_dw10_t dw10 = { 0 };
nvme_create_cq_dw11_t c_dw11 = { 0 };
nvme_create_sq_dw11_t s_dw11 = { 0 };
+ int ret;
dw10.b.q_qid = idx;
dw10.b.q_qsize = qp->nq_nentry - 1;
c_dw11.b.cq_pc = 1;
*** 1976,1997 ****
cmd->nc_sqe.sqe_opc = NVME_OPC_CREATE_CQUEUE;
cmd->nc_sqe.sqe_cdw10 = dw10.r;
cmd->nc_sqe.sqe_cdw11 = c_dw11.r;
cmd->nc_sqe.sqe_dptr.d_prp[0] = qp->nq_cqdma->nd_cookie.dmac_laddress;
! if (nvme_admin_cmd(cmd, nvme_admin_cmd_timeout) != DDI_SUCCESS) {
! dev_err(nvme->n_dip, CE_WARN,
! "!nvme_admin_cmd failed for CREATE CQUEUE");
! return (DDI_FAILURE);
! }
! if (nvme_check_cmd_status(cmd)) {
dev_err(nvme->n_dip, CE_WARN,
"!CREATE CQUEUE 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 (DDI_FAILURE);
}
nvme_free_cmd(cmd);
s_dw11.b.sq_pc = 1;
--- 1997,2013 ----
cmd->nc_sqe.sqe_opc = NVME_OPC_CREATE_CQUEUE;
cmd->nc_sqe.sqe_cdw10 = dw10.r;
cmd->nc_sqe.sqe_cdw11 = c_dw11.r;
cmd->nc_sqe.sqe_dptr.d_prp[0] = qp->nq_cqdma->nd_cookie.dmac_laddress;
! nvme_admin_cmd(cmd, nvme_admin_cmd_timeout);
! if ((ret = nvme_check_cmd_status(cmd)) != 0) {
dev_err(nvme->n_dip, CE_WARN,
"!CREATE CQUEUE failed with sct = %x, sc = %x",
cmd->nc_cqe.cqe_sf.sf_sct, cmd->nc_cqe.cqe_sf.sf_sc);
! goto fail;
}
nvme_free_cmd(cmd);
s_dw11.b.sq_pc = 1;
*** 2003,2029 ****
cmd->nc_sqe.sqe_opc = NVME_OPC_CREATE_SQUEUE;
cmd->nc_sqe.sqe_cdw10 = dw10.r;
cmd->nc_sqe.sqe_cdw11 = s_dw11.r;
cmd->nc_sqe.sqe_dptr.d_prp[0] = qp->nq_sqdma->nd_cookie.dmac_laddress;
! if (nvme_admin_cmd(cmd, nvme_admin_cmd_timeout) != DDI_SUCCESS) {
! dev_err(nvme->n_dip, CE_WARN,
! "!nvme_admin_cmd failed for CREATE SQUEUE");
! return (DDI_FAILURE);
! }
! if (nvme_check_cmd_status(cmd)) {
dev_err(nvme->n_dip, CE_WARN,
"!CREATE SQUEUE 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 (DDI_FAILURE);
}
nvme_free_cmd(cmd);
! return (DDI_SUCCESS);
}
static boolean_t
nvme_reset(nvme_t *nvme, boolean_t quiesce)
{
--- 2019,2041 ----
cmd->nc_sqe.sqe_opc = NVME_OPC_CREATE_SQUEUE;
cmd->nc_sqe.sqe_cdw10 = dw10.r;
cmd->nc_sqe.sqe_cdw11 = s_dw11.r;
cmd->nc_sqe.sqe_dptr.d_prp[0] = qp->nq_sqdma->nd_cookie.dmac_laddress;
! nvme_admin_cmd(cmd, nvme_admin_cmd_timeout);
! if ((ret = nvme_check_cmd_status(cmd)) != 0) {
dev_err(nvme->n_dip, CE_WARN,
"!CREATE SQUEUE failed with sct = %x, sc = %x",
cmd->nc_cqe.cqe_sf.sf_sct, cmd->nc_cqe.cqe_sf.sf_sc);
! goto fail;
}
+ fail:
nvme_free_cmd(cmd);
! return (ret);
}
static boolean_t
nvme_reset(nvme_t *nvme, boolean_t quiesce)
{
*** 2112,2124 ****
nvme_namespace_t *ns = &nvme->n_ns[nsid - 1];
nvme_identify_nsid_t *idns;
int last_rp;
ns->ns_nvme = nvme;
- idns = nvme_identify(nvme, nsid);
! if (idns == NULL) {
dev_err(nvme->n_dip, CE_WARN,
"!failed to identify namespace %d", nsid);
return (DDI_FAILURE);
}
--- 2124,2135 ----
nvme_namespace_t *ns = &nvme->n_ns[nsid - 1];
nvme_identify_nsid_t *idns;
int last_rp;
ns->ns_nvme = nvme;
! if (nvme_identify(nvme, nsid, (void **)&idns) != 0) {
dev_err(nvme->n_dip, CE_WARN,
"!failed to identify namespace %d", nsid);
return (DDI_FAILURE);
}
*** 2204,2214 ****
nvme_reg_acq_t acq = { 0 };
nvme_reg_cap_t cap;
nvme_reg_vs_t vs;
nvme_reg_csts_t csts;
int i = 0;
! int nqueues;
char model[sizeof (nvme->n_idctl->id_model) + 1];
char *vendor, *product;
/* Check controller version */
vs.r = nvme_get32(nvme, NVME_REG_VS);
--- 2215,2225 ----
nvme_reg_acq_t acq = { 0 };
nvme_reg_cap_t cap;
nvme_reg_vs_t vs;
nvme_reg_csts_t csts;
int i = 0;
! uint16_t nqueues;
char model[sizeof (nvme->n_idctl->id_model) + 1];
char *vendor, *product;
/* Check controller version */
vs.r = nvme_get32(nvme, NVME_REG_VS);
*** 2369,2380 ****
nvme_async_event(nvme);
/*
* Identify Controller
*/
! nvme->n_idctl = nvme_identify(nvme, 0);
! if (nvme->n_idctl == NULL) {
dev_err(nvme->n_dip, CE_WARN,
"!failed to identify controller");
goto fail;
}
--- 2380,2390 ----
nvme_async_event(nvme);
/*
* Identify Controller
*/
! if (nvme_identify(nvme, 0, (void **)&nvme->n_idctl) != 0) {
dev_err(nvme->n_dip, CE_WARN,
"!failed to identify controller");
goto fail;
}
*** 2459,2469 ****
"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");
/*
* Assume the cache is (still) enabled.
--- 2469,2480 ----
"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)
! != 0) {
dev_err(nvme->n_dip, CE_WARN,
"!failed to %sable volatile write cache",
nvme->n_write_cache_enabled ? "en" : "dis");
/*
* Assume the cache is (still) enabled.
*** 2531,2560 ****
nqueues = nvme->n_intr_cnt;
/*
* Create I/O queue pairs.
*/
! nvme->n_ioq_count = nvme_set_nqueues(nvme, nqueues);
! if (nvme->n_ioq_count == 0) {
dev_err(nvme->n_dip, CE_WARN,
! "!failed to set number of I/O queues to %d", nqueues);
goto fail;
}
/*
* Reallocate I/O queue array
*/
kmem_free(nvme->n_ioq, sizeof (nvme_qpair_t *));
nvme->n_ioq = kmem_zalloc(sizeof (nvme_qpair_t *) *
! (nvme->n_ioq_count + 1), KM_SLEEP);
nvme->n_ioq[0] = nvme->n_adminq;
/*
* If we got less queues than we asked for we might as well give
* some of the interrupt vectors back to the system.
*/
! if (nvme->n_ioq_count < nqueues) {
nvme_release_interrupts(nvme);
if (nvme_setup_interrupts(nvme, nvme->n_intr_type,
nvme->n_ioq_count) != DDI_SUCCESS) {
dev_err(nvme->n_dip, CE_WARN,
--- 2542,2574 ----
nqueues = nvme->n_intr_cnt;
/*
* Create I/O queue pairs.
*/
!
! if (nvme_set_nqueues(nvme, &nqueues) != 0) {
dev_err(nvme->n_dip, CE_WARN,
! "!failed to set number of I/O queues to %d",
! nvme->n_intr_cnt);
goto fail;
}
/*
* Reallocate I/O queue array
*/
kmem_free(nvme->n_ioq, sizeof (nvme_qpair_t *));
nvme->n_ioq = kmem_zalloc(sizeof (nvme_qpair_t *) *
! (nqueues + 1), KM_SLEEP);
nvme->n_ioq[0] = nvme->n_adminq;
+ nvme->n_ioq_count = nqueues;
+
/*
* If we got less queues than we asked for we might as well give
* some of the interrupt vectors back to the system.
*/
! if (nvme->n_ioq_count < nvme->n_intr_cnt) {
nvme_release_interrupts(nvme);
if (nvme_setup_interrupts(nvme, nvme->n_intr_type,
nvme->n_ioq_count) != DDI_SUCCESS) {
dev_err(nvme->n_dip, CE_WARN,
*** 2577,2588 ****
dev_err(nvme->n_dip, CE_WARN,
"!unable to allocate I/O qpair %d", i);
goto fail;
}
! if (nvme_create_io_qpair(nvme, nvme->n_ioq[i], i)
! != DDI_SUCCESS) {
dev_err(nvme->n_dip, CE_WARN,
"!unable to create I/O qpair %d", i);
goto fail;
}
}
--- 2591,2601 ----
dev_err(nvme->n_dip, CE_WARN,
"!unable to allocate I/O qpair %d", i);
goto fail;
}
! if (nvme_create_io_qpair(nvme, nvme->n_ioq[i], i) != 0) {
dev_err(nvme->n_dip, CE_WARN,
"!unable to create I/O qpair %d", i);
goto fail;
}
}
*** 2612,2621 ****
--- 2625,2638 ----
nvme_cmd_t *cmd;
if (inum >= nvme->n_intr_cnt)
return (DDI_INTR_UNCLAIMED);
+ if (nvme->n_dead)
+ return (nvme->n_intr_type == DDI_INTR_TYPE_FIXED ?
+ DDI_INTR_UNCLAIMED : DDI_INTR_CLAIMED);
+
/*
* The interrupt vector a queue uses is calculated as queue_idx %
* intr_cnt in nvme_create_io_qpair(). Iterate through the queue array
* in steps of n_intr_cnt to process all queues using this vector.
*/
*** 3373,3382 ****
--- 3390,3402 ----
return (ENXIO);
if (nsid > nvme->n_namespace_count)
return (ENXIO);
+ if (nvme->n_dead)
+ return (EIO);
+
nm = nsid == 0 ? &nvme->n_minor : &nvme->n_ns[nsid - 1].ns_minor;
mutex_enter(&nm->nm_mutex);
if (nm->nm_oexcl) {
rv = EBUSY;
*** 3445,3457 ****
return (EPERM);
if (nioc->n_len < NVME_IDENTIFY_BUFSIZE)
return (EINVAL);
! idctl = nvme_identify(nvme, nsid);
! if (idctl == NULL)
! return (EIO);
if (ddi_copyout(idctl, (void *)nioc->n_buf, NVME_IDENTIFY_BUFSIZE, mode)
!= 0)
rv = EFAULT;
--- 3465,3476 ----
return (EPERM);
if (nioc->n_len < NVME_IDENTIFY_BUFSIZE)
return (EINVAL);
! if ((rv = nvme_identify(nvme, nsid, (void **)&idctl)) != 0)
! return (rv);
if (ddi_copyout(idctl, (void *)nioc->n_buf, NVME_IDENTIFY_BUFSIZE, mode)
!= 0)
rv = EFAULT;
*** 3614,3626 ****
default:
return (EINVAL);
}
! if (nvme_get_features(nvme, nsid, feature, &res, &buf, &bufsize) ==
! B_FALSE)
! return (EIO);
if (nioc->n_len < bufsize) {
kmem_free(buf, bufsize);
return (EINVAL);
}
--- 3633,3645 ----
default:
return (EINVAL);
}
! rv = nvme_get_features(nvme, nsid, feature, &res, &buf, &bufsize);
! if (rv != 0)
! return (rv);
if (nioc->n_len < bufsize) {
kmem_free(buf, bufsize);
return (EINVAL);
}
*** 3833,3842 ****
--- 3852,3865 ----
#ifdef _MULTI_DATAMODEL
break;
}
#endif
+ if (nvme->n_dead && cmd != NVME_IOC_DETACH)
+ return (EIO);
+
+
if (cmd == NVME_IOC_IDENTIFY_CTRL) {
/*
* This makes NVME_IOC_IDENTIFY_CTRL work the same on devctl and
* attachment point nodes.
*/