nvme/pcie: move the common IO path APIs to nvme_pcie_common.c
The data path for PCIe and vfio-user transports are almost same too, so move the code from nvme_pcie.c to nvme_pcie_common.c, so that these APIs can be reused by vfio_user. No logic change for this patch. Change-Id: I82f480bba3bae0ce35e2a98f29839081095f7d50 Signed-off-by: Changpeng Liu <changpeng.liu@intel.com> Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/6040 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Reviewed-by: <dongx.yi@intel.com> Reviewed-by: Aleksey Marchuk <alexeymar@mellanox.com>
This commit is contained in:
parent
613f82f547
commit
8b203d1994
@ -52,9 +52,7 @@ struct nvme_pcie_enum_ctx {
|
|||||||
|
|
||||||
static int nvme_pcie_ctrlr_attach(struct spdk_nvme_probe_ctx *probe_ctx,
|
static int nvme_pcie_ctrlr_attach(struct spdk_nvme_probe_ctx *probe_ctx,
|
||||||
struct spdk_pci_addr *pci_addr);
|
struct spdk_pci_addr *pci_addr);
|
||||||
static int nvme_pcie_qpair_destroy(struct spdk_nvme_qpair *qpair);
|
|
||||||
|
|
||||||
__thread struct nvme_pcie_ctrlr *g_thread_mmio_ctrlr = NULL;
|
|
||||||
static uint16_t g_signal_lock;
|
static uint16_t g_signal_lock;
|
||||||
static bool g_sigset = false;
|
static bool g_sigset = false;
|
||||||
|
|
||||||
@ -755,245 +753,6 @@ nvme_pcie_ctrlr_destruct(struct spdk_nvme_ctrlr *ctrlr)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Used when dst points to MMIO (i.e. CMB) in a virtual machine - in these cases we must
|
|
||||||
* not use wide instructions because QEMU will not emulate such instructions to MMIO space.
|
|
||||||
* So this function ensures we only copy 8 bytes at a time.
|
|
||||||
*/
|
|
||||||
static inline void
|
|
||||||
nvme_pcie_copy_command_mmio(struct spdk_nvme_cmd *dst, const struct spdk_nvme_cmd *src)
|
|
||||||
{
|
|
||||||
uint64_t *dst64 = (uint64_t *)dst;
|
|
||||||
const uint64_t *src64 = (const uint64_t *)src;
|
|
||||||
uint32_t i;
|
|
||||||
|
|
||||||
for (i = 0; i < sizeof(*dst) / 8; i++) {
|
|
||||||
dst64[i] = src64[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
nvme_pcie_copy_command(struct spdk_nvme_cmd *dst, const struct spdk_nvme_cmd *src)
|
|
||||||
{
|
|
||||||
/* dst and src are known to be non-overlapping and 64-byte aligned. */
|
|
||||||
#if defined(__SSE2__)
|
|
||||||
__m128i *d128 = (__m128i *)dst;
|
|
||||||
const __m128i *s128 = (const __m128i *)src;
|
|
||||||
|
|
||||||
_mm_stream_si128(&d128[0], _mm_load_si128(&s128[0]));
|
|
||||||
_mm_stream_si128(&d128[1], _mm_load_si128(&s128[1]));
|
|
||||||
_mm_stream_si128(&d128[2], _mm_load_si128(&s128[2]));
|
|
||||||
_mm_stream_si128(&d128[3], _mm_load_si128(&s128[3]));
|
|
||||||
#else
|
|
||||||
*dst = *src;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int
|
|
||||||
nvme_pcie_qpair_need_event(uint16_t event_idx, uint16_t new_idx, uint16_t old)
|
|
||||||
{
|
|
||||||
return (uint16_t)(new_idx - event_idx) <= (uint16_t)(new_idx - old);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
nvme_pcie_qpair_update_mmio_required(struct spdk_nvme_qpair *qpair, uint16_t value,
|
|
||||||
volatile uint32_t *shadow_db,
|
|
||||||
volatile uint32_t *eventidx)
|
|
||||||
{
|
|
||||||
uint16_t old;
|
|
||||||
|
|
||||||
if (!shadow_db) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
old = *shadow_db;
|
|
||||||
*shadow_db = value;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Ensure that the doorbell is updated before reading the EventIdx from
|
|
||||||
* memory
|
|
||||||
*/
|
|
||||||
spdk_mb();
|
|
||||||
|
|
||||||
if (!nvme_pcie_qpair_need_event(*eventidx, value, old)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
nvme_pcie_qpair_ring_sq_doorbell(struct spdk_nvme_qpair *qpair)
|
|
||||||
{
|
|
||||||
struct nvme_pcie_qpair *pqpair = nvme_pcie_qpair(qpair);
|
|
||||||
struct nvme_pcie_ctrlr *pctrlr = nvme_pcie_ctrlr(qpair->ctrlr);
|
|
||||||
bool need_mmio = true;
|
|
||||||
|
|
||||||
if (qpair->first_fused_submitted) {
|
|
||||||
/* This is first cmd of two fused commands - don't ring doorbell */
|
|
||||||
qpair->first_fused_submitted = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spdk_unlikely(pqpair->flags.has_shadow_doorbell)) {
|
|
||||||
need_mmio = nvme_pcie_qpair_update_mmio_required(qpair,
|
|
||||||
pqpair->sq_tail,
|
|
||||||
pqpair->shadow_doorbell.sq_tdbl,
|
|
||||||
pqpair->shadow_doorbell.sq_eventidx);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spdk_likely(need_mmio)) {
|
|
||||||
spdk_wmb();
|
|
||||||
g_thread_mmio_ctrlr = pctrlr;
|
|
||||||
spdk_mmio_write_4(pqpair->sq_tdbl, pqpair->sq_tail);
|
|
||||||
g_thread_mmio_ctrlr = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
nvme_pcie_qpair_ring_cq_doorbell(struct spdk_nvme_qpair *qpair)
|
|
||||||
{
|
|
||||||
struct nvme_pcie_qpair *pqpair = nvme_pcie_qpair(qpair);
|
|
||||||
struct nvme_pcie_ctrlr *pctrlr = nvme_pcie_ctrlr(qpair->ctrlr);
|
|
||||||
bool need_mmio = true;
|
|
||||||
|
|
||||||
if (spdk_unlikely(pqpair->flags.has_shadow_doorbell)) {
|
|
||||||
need_mmio = nvme_pcie_qpair_update_mmio_required(qpair,
|
|
||||||
pqpair->cq_head,
|
|
||||||
pqpair->shadow_doorbell.cq_hdbl,
|
|
||||||
pqpair->shadow_doorbell.cq_eventidx);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spdk_likely(need_mmio)) {
|
|
||||||
g_thread_mmio_ctrlr = pctrlr;
|
|
||||||
spdk_mmio_write_4(pqpair->cq_hdbl, pqpair->cq_head);
|
|
||||||
g_thread_mmio_ctrlr = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
nvme_pcie_qpair_submit_tracker(struct spdk_nvme_qpair *qpair, struct nvme_tracker *tr)
|
|
||||||
{
|
|
||||||
struct nvme_request *req;
|
|
||||||
struct nvme_pcie_qpair *pqpair = nvme_pcie_qpair(qpair);
|
|
||||||
struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
|
|
||||||
|
|
||||||
req = tr->req;
|
|
||||||
assert(req != NULL);
|
|
||||||
|
|
||||||
if (req->cmd.fuse == SPDK_NVME_IO_FLAGS_FUSE_FIRST) {
|
|
||||||
/* This is first cmd of two fused commands - don't ring doorbell */
|
|
||||||
qpair->first_fused_submitted = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Don't use wide instructions to copy NVMe command, this is limited by QEMU
|
|
||||||
* virtual NVMe controller, the maximum access width is 8 Bytes for one time.
|
|
||||||
*/
|
|
||||||
if (spdk_unlikely((ctrlr->quirks & NVME_QUIRK_MAXIMUM_PCI_ACCESS_WIDTH) && pqpair->sq_in_cmb)) {
|
|
||||||
nvme_pcie_copy_command_mmio(&pqpair->cmd[pqpair->sq_tail], &req->cmd);
|
|
||||||
} else {
|
|
||||||
/* Copy the command from the tracker to the submission queue. */
|
|
||||||
nvme_pcie_copy_command(&pqpair->cmd[pqpair->sq_tail], &req->cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spdk_unlikely(++pqpair->sq_tail == pqpair->num_entries)) {
|
|
||||||
pqpair->sq_tail = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spdk_unlikely(pqpair->sq_tail == pqpair->sq_head)) {
|
|
||||||
SPDK_ERRLOG("sq_tail is passing sq_head!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pqpair->flags.delay_cmd_submit) {
|
|
||||||
nvme_pcie_qpair_ring_sq_doorbell(qpair);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
nvme_pcie_qpair_complete_tracker(struct spdk_nvme_qpair *qpair, struct nvme_tracker *tr,
|
|
||||||
struct spdk_nvme_cpl *cpl, bool print_on_error)
|
|
||||||
{
|
|
||||||
struct nvme_pcie_qpair *pqpair = nvme_pcie_qpair(qpair);
|
|
||||||
struct nvme_request *req;
|
|
||||||
bool retry, error;
|
|
||||||
bool req_from_current_proc = true;
|
|
||||||
|
|
||||||
req = tr->req;
|
|
||||||
|
|
||||||
assert(req != NULL);
|
|
||||||
|
|
||||||
error = spdk_nvme_cpl_is_error(cpl);
|
|
||||||
retry = error && nvme_completion_is_retry(cpl) &&
|
|
||||||
req->retries < pqpair->retry_count;
|
|
||||||
|
|
||||||
if (error && print_on_error && !qpair->ctrlr->opts.disable_error_logging) {
|
|
||||||
spdk_nvme_qpair_print_command(qpair, &req->cmd);
|
|
||||||
spdk_nvme_qpair_print_completion(qpair, cpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(cpl->cid == req->cmd.cid);
|
|
||||||
|
|
||||||
if (retry) {
|
|
||||||
req->retries++;
|
|
||||||
nvme_pcie_qpair_submit_tracker(qpair, tr);
|
|
||||||
} else {
|
|
||||||
TAILQ_REMOVE(&pqpair->outstanding_tr, tr, tq_list);
|
|
||||||
|
|
||||||
/* Only check admin requests from different processes. */
|
|
||||||
if (nvme_qpair_is_admin_queue(qpair) && req->pid != getpid()) {
|
|
||||||
req_from_current_proc = false;
|
|
||||||
nvme_pcie_qpair_insert_pending_admin_request(qpair, req, cpl);
|
|
||||||
} else {
|
|
||||||
nvme_complete_request(tr->cb_fn, tr->cb_arg, qpair, req, cpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req_from_current_proc == true) {
|
|
||||||
nvme_qpair_free_request(qpair, req);
|
|
||||||
}
|
|
||||||
|
|
||||||
tr->req = NULL;
|
|
||||||
|
|
||||||
TAILQ_INSERT_HEAD(&pqpair->free_tr, tr, tq_list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
nvme_pcie_qpair_manual_complete_tracker(struct spdk_nvme_qpair *qpair,
|
|
||||||
struct nvme_tracker *tr, uint32_t sct, uint32_t sc, uint32_t dnr,
|
|
||||||
bool print_on_error)
|
|
||||||
{
|
|
||||||
struct spdk_nvme_cpl cpl;
|
|
||||||
|
|
||||||
memset(&cpl, 0, sizeof(cpl));
|
|
||||||
cpl.sqid = qpair->id;
|
|
||||||
cpl.cid = tr->cid;
|
|
||||||
cpl.status.sct = sct;
|
|
||||||
cpl.status.sc = sc;
|
|
||||||
cpl.status.dnr = dnr;
|
|
||||||
nvme_pcie_qpair_complete_tracker(qpair, tr, &cpl, print_on_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
nvme_pcie_qpair_abort_trackers(struct spdk_nvme_qpair *qpair, uint32_t dnr)
|
|
||||||
{
|
|
||||||
struct nvme_pcie_qpair *pqpair = nvme_pcie_qpair(qpair);
|
|
||||||
struct nvme_tracker *tr, *temp, *last;
|
|
||||||
|
|
||||||
last = TAILQ_LAST(&pqpair->outstanding_tr, nvme_outstanding_tr_head);
|
|
||||||
|
|
||||||
/* Abort previously submitted (outstanding) trs */
|
|
||||||
TAILQ_FOREACH_SAFE(tr, &pqpair->outstanding_tr, tq_list, temp) {
|
|
||||||
if (!qpair->ctrlr->opts.disable_error_logging) {
|
|
||||||
SPDK_ERRLOG("aborting outstanding command\n");
|
|
||||||
}
|
|
||||||
nvme_pcie_qpair_manual_complete_tracker(qpair, tr, SPDK_NVME_SCT_GENERIC,
|
|
||||||
SPDK_NVME_SC_ABORTED_BY_REQUEST, dnr, true);
|
|
||||||
|
|
||||||
if (tr == last) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
nvme_pcie_qpair_iterate_requests(struct spdk_nvme_qpair *qpair,
|
nvme_pcie_qpair_iterate_requests(struct spdk_nvme_qpair *qpair,
|
||||||
int (*iter_fn)(struct nvme_request *req, void *arg),
|
int (*iter_fn)(struct nvme_request *req, void *arg),
|
||||||
@ -1017,172 +776,6 @@ nvme_pcie_qpair_iterate_requests(struct spdk_nvme_qpair *qpair,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
nvme_pcie_admin_qpair_abort_aers(struct spdk_nvme_qpair *qpair)
|
|
||||||
{
|
|
||||||
struct nvme_pcie_qpair *pqpair = nvme_pcie_qpair(qpair);
|
|
||||||
struct nvme_tracker *tr;
|
|
||||||
|
|
||||||
tr = TAILQ_FIRST(&pqpair->outstanding_tr);
|
|
||||||
while (tr != NULL) {
|
|
||||||
assert(tr->req != NULL);
|
|
||||||
if (tr->req->cmd.opc == SPDK_NVME_OPC_ASYNC_EVENT_REQUEST) {
|
|
||||||
nvme_pcie_qpair_manual_complete_tracker(qpair, tr,
|
|
||||||
SPDK_NVME_SCT_GENERIC, SPDK_NVME_SC_ABORTED_SQ_DELETION, 0,
|
|
||||||
false);
|
|
||||||
tr = TAILQ_FIRST(&pqpair->outstanding_tr);
|
|
||||||
} else {
|
|
||||||
tr = TAILQ_NEXT(tr, tq_list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
nvme_pcie_admin_qpair_destroy(struct spdk_nvme_qpair *qpair)
|
|
||||||
{
|
|
||||||
nvme_pcie_admin_qpair_abort_aers(qpair);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
nvme_pcie_qpair_destroy(struct spdk_nvme_qpair *qpair)
|
|
||||||
{
|
|
||||||
struct nvme_pcie_qpair *pqpair = nvme_pcie_qpair(qpair);
|
|
||||||
|
|
||||||
if (nvme_qpair_is_admin_queue(qpair)) {
|
|
||||||
nvme_pcie_admin_qpair_destroy(qpair);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* We check sq_vaddr and cq_vaddr to see if the user specified the memory
|
|
||||||
* buffers when creating the I/O queue.
|
|
||||||
* If the user specified them, we cannot free that memory.
|
|
||||||
* Nor do we free it if it's in the CMB.
|
|
||||||
*/
|
|
||||||
if (!pqpair->sq_vaddr && pqpair->cmd && !pqpair->sq_in_cmb) {
|
|
||||||
spdk_free(pqpair->cmd);
|
|
||||||
}
|
|
||||||
if (!pqpair->cq_vaddr && pqpair->cpl) {
|
|
||||||
spdk_free(pqpair->cpl);
|
|
||||||
}
|
|
||||||
if (pqpair->tr) {
|
|
||||||
spdk_free(pqpair->tr);
|
|
||||||
}
|
|
||||||
|
|
||||||
nvme_qpair_deinit(qpair);
|
|
||||||
|
|
||||||
spdk_free(pqpair);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
nvme_pcie_qpair_abort_reqs(struct spdk_nvme_qpair *qpair, uint32_t dnr)
|
|
||||||
{
|
|
||||||
nvme_pcie_qpair_abort_trackers(qpair, dnr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct spdk_nvme_qpair *
|
|
||||||
nvme_pcie_ctrlr_create_io_qpair(struct spdk_nvme_ctrlr *ctrlr, uint16_t qid,
|
|
||||||
const struct spdk_nvme_io_qpair_opts *opts)
|
|
||||||
{
|
|
||||||
struct nvme_pcie_qpair *pqpair;
|
|
||||||
struct spdk_nvme_qpair *qpair;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
assert(ctrlr != NULL);
|
|
||||||
|
|
||||||
pqpair = spdk_zmalloc(sizeof(*pqpair), 64, NULL,
|
|
||||||
SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_SHARE);
|
|
||||||
if (pqpair == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
pqpair->num_entries = opts->io_queue_size;
|
|
||||||
pqpair->flags.delay_cmd_submit = opts->delay_cmd_submit;
|
|
||||||
|
|
||||||
qpair = &pqpair->qpair;
|
|
||||||
|
|
||||||
rc = nvme_qpair_init(qpair, qid, ctrlr, opts->qprio, opts->io_queue_requests);
|
|
||||||
if (rc != 0) {
|
|
||||||
nvme_pcie_qpair_destroy(qpair);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = nvme_pcie_qpair_construct(qpair, opts);
|
|
||||||
|
|
||||||
if (rc != 0) {
|
|
||||||
nvme_pcie_qpair_destroy(qpair);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return qpair;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int32_t nvme_pcie_qpair_process_completions(struct spdk_nvme_qpair *qpair,
|
|
||||||
uint32_t max_completions);
|
|
||||||
|
|
||||||
static int
|
|
||||||
nvme_pcie_ctrlr_delete_io_qpair(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair)
|
|
||||||
{
|
|
||||||
struct nvme_completion_poll_status *status;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
assert(ctrlr != NULL);
|
|
||||||
|
|
||||||
if (ctrlr->is_removed) {
|
|
||||||
goto free;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = calloc(1, sizeof(*status));
|
|
||||||
if (!status) {
|
|
||||||
SPDK_ERRLOG("Failed to allocate status tracker\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Delete the I/O submission queue */
|
|
||||||
rc = nvme_pcie_ctrlr_cmd_delete_io_sq(ctrlr, qpair, nvme_completion_poll_cb, status);
|
|
||||||
if (rc != 0) {
|
|
||||||
SPDK_ERRLOG("Failed to send request to delete_io_sq with rc=%d\n", rc);
|
|
||||||
free(status);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
if (nvme_wait_for_completion(ctrlr->adminq, status)) {
|
|
||||||
if (!status->timed_out) {
|
|
||||||
free(status);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now that the submission queue is deleted, the device is supposed to have
|
|
||||||
* completed any outstanding I/O. Try to complete them. If they don't complete,
|
|
||||||
* they'll be marked as aborted and completed below. */
|
|
||||||
nvme_pcie_qpair_process_completions(qpair, 0);
|
|
||||||
|
|
||||||
memset(status, 0, sizeof(*status));
|
|
||||||
/* Delete the completion queue */
|
|
||||||
rc = nvme_pcie_ctrlr_cmd_delete_io_cq(ctrlr, qpair, nvme_completion_poll_cb, status);
|
|
||||||
if (rc != 0) {
|
|
||||||
SPDK_ERRLOG("Failed to send request to delete_io_cq with rc=%d\n", rc);
|
|
||||||
free(status);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
if (nvme_wait_for_completion(ctrlr->adminq, status)) {
|
|
||||||
if (!status->timed_out) {
|
|
||||||
free(status);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
free(status);
|
|
||||||
|
|
||||||
free:
|
|
||||||
if (qpair->no_deletion_notification_needed == 0) {
|
|
||||||
/* Abort the rest of the I/O */
|
|
||||||
nvme_pcie_qpair_abort_trackers(qpair, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
nvme_pcie_qpair_destroy(qpair);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
nvme_pcie_fail_request_bad_vtophys(struct spdk_nvme_qpair *qpair, struct nvme_tracker *tr)
|
nvme_pcie_fail_request_bad_vtophys(struct spdk_nvme_qpair *qpair, struct nvme_tracker *tr)
|
||||||
{
|
{
|
||||||
@ -1695,154 +1288,6 @@ exit:
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
nvme_pcie_qpair_check_timeout(struct spdk_nvme_qpair *qpair)
|
|
||||||
{
|
|
||||||
uint64_t t02;
|
|
||||||
struct nvme_tracker *tr, *tmp;
|
|
||||||
struct nvme_pcie_qpair *pqpair = nvme_pcie_qpair(qpair);
|
|
||||||
struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
|
|
||||||
struct spdk_nvme_ctrlr_process *active_proc;
|
|
||||||
|
|
||||||
/* Don't check timeouts during controller initialization. */
|
|
||||||
if (ctrlr->state != NVME_CTRLR_STATE_READY) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nvme_qpair_is_admin_queue(qpair)) {
|
|
||||||
active_proc = nvme_ctrlr_get_current_process(ctrlr);
|
|
||||||
} else {
|
|
||||||
active_proc = qpair->active_proc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Only check timeouts if the current process has a timeout callback. */
|
|
||||||
if (active_proc == NULL || active_proc->timeout_cb_fn == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
t02 = spdk_get_ticks();
|
|
||||||
TAILQ_FOREACH_SAFE(tr, &pqpair->outstanding_tr, tq_list, tmp) {
|
|
||||||
assert(tr->req != NULL);
|
|
||||||
|
|
||||||
if (nvme_request_check_timeout(tr->req, tr->cid, active_proc, t02)) {
|
|
||||||
/*
|
|
||||||
* The requests are in order, so as soon as one has not timed out,
|
|
||||||
* stop iterating.
|
|
||||||
*/
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int32_t
|
|
||||||
nvme_pcie_qpair_process_completions(struct spdk_nvme_qpair *qpair, uint32_t max_completions)
|
|
||||||
{
|
|
||||||
struct nvme_pcie_qpair *pqpair = nvme_pcie_qpair(qpair);
|
|
||||||
struct nvme_tracker *tr;
|
|
||||||
struct spdk_nvme_cpl *cpl, *next_cpl;
|
|
||||||
uint32_t num_completions = 0;
|
|
||||||
struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
|
|
||||||
uint16_t next_cq_head;
|
|
||||||
uint8_t next_phase;
|
|
||||||
bool next_is_valid = false;
|
|
||||||
|
|
||||||
if (spdk_unlikely(nvme_qpair_is_admin_queue(qpair))) {
|
|
||||||
nvme_robust_mutex_lock(&ctrlr->ctrlr_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (max_completions == 0 || max_completions > pqpair->max_completions_cap) {
|
|
||||||
/*
|
|
||||||
* max_completions == 0 means unlimited, but complete at most
|
|
||||||
* max_completions_cap batch of I/O at a time so that the completion
|
|
||||||
* queue doorbells don't wrap around.
|
|
||||||
*/
|
|
||||||
max_completions = pqpair->max_completions_cap;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
cpl = &pqpair->cpl[pqpair->cq_head];
|
|
||||||
|
|
||||||
if (!next_is_valid && cpl->status.p != pqpair->flags.phase) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spdk_likely(pqpair->cq_head + 1 != pqpair->num_entries)) {
|
|
||||||
next_cq_head = pqpair->cq_head + 1;
|
|
||||||
next_phase = pqpair->flags.phase;
|
|
||||||
} else {
|
|
||||||
next_cq_head = 0;
|
|
||||||
next_phase = !pqpair->flags.phase;
|
|
||||||
}
|
|
||||||
next_cpl = &pqpair->cpl[next_cq_head];
|
|
||||||
next_is_valid = (next_cpl->status.p == next_phase);
|
|
||||||
if (next_is_valid) {
|
|
||||||
__builtin_prefetch(&pqpair->tr[next_cpl->cid]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __PPC64__
|
|
||||||
/*
|
|
||||||
* This memory barrier prevents reordering of:
|
|
||||||
* - load after store from/to tr
|
|
||||||
* - load after load cpl phase and cpl cid
|
|
||||||
*/
|
|
||||||
spdk_mb();
|
|
||||||
#elif defined(__aarch64__)
|
|
||||||
__asm volatile("dmb oshld" ::: "memory");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (spdk_unlikely(++pqpair->cq_head == pqpair->num_entries)) {
|
|
||||||
pqpair->cq_head = 0;
|
|
||||||
pqpair->flags.phase = !pqpair->flags.phase;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr = &pqpair->tr[cpl->cid];
|
|
||||||
/* Prefetch the req's STAILQ_ENTRY since we'll need to access it
|
|
||||||
* as part of putting the req back on the qpair's free list.
|
|
||||||
*/
|
|
||||||
__builtin_prefetch(&tr->req->stailq);
|
|
||||||
pqpair->sq_head = cpl->sqhd;
|
|
||||||
|
|
||||||
if (tr->req) {
|
|
||||||
nvme_pcie_qpair_complete_tracker(qpair, tr, cpl, true);
|
|
||||||
} else {
|
|
||||||
SPDK_ERRLOG("cpl does not map to outstanding cmd\n");
|
|
||||||
spdk_nvme_qpair_print_completion(qpair, cpl);
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (++num_completions == max_completions) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (num_completions > 0) {
|
|
||||||
nvme_pcie_qpair_ring_cq_doorbell(qpair);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pqpair->flags.delay_cmd_submit) {
|
|
||||||
if (pqpair->last_sq_tail != pqpair->sq_tail) {
|
|
||||||
nvme_pcie_qpair_ring_sq_doorbell(qpair);
|
|
||||||
pqpair->last_sq_tail = pqpair->sq_tail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spdk_unlikely(ctrlr->timeout_enabled)) {
|
|
||||||
/*
|
|
||||||
* User registered for timeout callback
|
|
||||||
*/
|
|
||||||
nvme_pcie_qpair_check_timeout(qpair);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Before returning, complete any pending admin request. */
|
|
||||||
if (spdk_unlikely(nvme_qpair_is_admin_queue(qpair))) {
|
|
||||||
nvme_pcie_qpair_complete_pending_admin_request(qpair);
|
|
||||||
|
|
||||||
nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
return num_completions;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct spdk_pci_id nvme_pci_driver_id[] = {
|
static struct spdk_pci_id nvme_pci_driver_id[] = {
|
||||||
{
|
{
|
||||||
.class_id = SPDK_PCI_CLASS_NVME,
|
.class_id = SPDK_PCI_CLASS_NVME,
|
||||||
|
@ -40,6 +40,8 @@
|
|||||||
#include "nvme_internal.h"
|
#include "nvme_internal.h"
|
||||||
#include "nvme_pcie_internal.h"
|
#include "nvme_pcie_internal.h"
|
||||||
|
|
||||||
|
__thread struct nvme_pcie_ctrlr *g_thread_mmio_ctrlr = NULL;
|
||||||
|
|
||||||
static uint64_t
|
static uint64_t
|
||||||
nvme_pcie_vtophys(struct spdk_nvme_ctrlr *ctrlr, const void *buf)
|
nvme_pcie_vtophys(struct spdk_nvme_ctrlr *ctrlr, const void *buf)
|
||||||
{
|
{
|
||||||
@ -526,6 +528,474 @@ nvme_pcie_ctrlr_disconnect_qpair(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Used when dst points to MMIO (i.e. CMB) in a virtual machine - in these cases we must
|
||||||
|
* not use wide instructions because QEMU will not emulate such instructions to MMIO space.
|
||||||
|
* So this function ensures we only copy 8 bytes at a time.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
nvme_pcie_copy_command_mmio(struct spdk_nvme_cmd *dst, const struct spdk_nvme_cmd *src)
|
||||||
|
{
|
||||||
|
uint64_t *dst64 = (uint64_t *)dst;
|
||||||
|
const uint64_t *src64 = (const uint64_t *)src;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(*dst) / 8; i++) {
|
||||||
|
dst64[i] = src64[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
nvme_pcie_copy_command(struct spdk_nvme_cmd *dst, const struct spdk_nvme_cmd *src)
|
||||||
|
{
|
||||||
|
/* dst and src are known to be non-overlapping and 64-byte aligned. */
|
||||||
|
#if defined(__SSE2__)
|
||||||
|
__m128i *d128 = (__m128i *)dst;
|
||||||
|
const __m128i *s128 = (const __m128i *)src;
|
||||||
|
|
||||||
|
_mm_stream_si128(&d128[0], _mm_load_si128(&s128[0]));
|
||||||
|
_mm_stream_si128(&d128[1], _mm_load_si128(&s128[1]));
|
||||||
|
_mm_stream_si128(&d128[2], _mm_load_si128(&s128[2]));
|
||||||
|
_mm_stream_si128(&d128[3], _mm_load_si128(&s128[3]));
|
||||||
|
#else
|
||||||
|
*dst = *src;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nvme_pcie_qpair_submit_tracker(struct spdk_nvme_qpair *qpair, struct nvme_tracker *tr)
|
||||||
|
{
|
||||||
|
struct nvme_request *req;
|
||||||
|
struct nvme_pcie_qpair *pqpair = nvme_pcie_qpair(qpair);
|
||||||
|
struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
|
||||||
|
|
||||||
|
req = tr->req;
|
||||||
|
assert(req != NULL);
|
||||||
|
|
||||||
|
if (req->cmd.fuse == SPDK_NVME_IO_FLAGS_FUSE_FIRST) {
|
||||||
|
/* This is first cmd of two fused commands - don't ring doorbell */
|
||||||
|
qpair->first_fused_submitted = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Don't use wide instructions to copy NVMe command, this is limited by QEMU
|
||||||
|
* virtual NVMe controller, the maximum access width is 8 Bytes for one time.
|
||||||
|
*/
|
||||||
|
if (spdk_unlikely((ctrlr->quirks & NVME_QUIRK_MAXIMUM_PCI_ACCESS_WIDTH) && pqpair->sq_in_cmb)) {
|
||||||
|
nvme_pcie_copy_command_mmio(&pqpair->cmd[pqpair->sq_tail], &req->cmd);
|
||||||
|
} else {
|
||||||
|
/* Copy the command from the tracker to the submission queue. */
|
||||||
|
nvme_pcie_copy_command(&pqpair->cmd[pqpair->sq_tail], &req->cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spdk_unlikely(++pqpair->sq_tail == pqpair->num_entries)) {
|
||||||
|
pqpair->sq_tail = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spdk_unlikely(pqpair->sq_tail == pqpair->sq_head)) {
|
||||||
|
SPDK_ERRLOG("sq_tail is passing sq_head!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pqpair->flags.delay_cmd_submit) {
|
||||||
|
nvme_pcie_qpair_ring_sq_doorbell(qpair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nvme_pcie_qpair_complete_tracker(struct spdk_nvme_qpair *qpair, struct nvme_tracker *tr,
|
||||||
|
struct spdk_nvme_cpl *cpl, bool print_on_error)
|
||||||
|
{
|
||||||
|
struct nvme_pcie_qpair *pqpair = nvme_pcie_qpair(qpair);
|
||||||
|
struct nvme_request *req;
|
||||||
|
bool retry, error;
|
||||||
|
bool req_from_current_proc = true;
|
||||||
|
|
||||||
|
req = tr->req;
|
||||||
|
|
||||||
|
assert(req != NULL);
|
||||||
|
|
||||||
|
error = spdk_nvme_cpl_is_error(cpl);
|
||||||
|
retry = error && nvme_completion_is_retry(cpl) &&
|
||||||
|
req->retries < pqpair->retry_count;
|
||||||
|
|
||||||
|
if (error && print_on_error && !qpair->ctrlr->opts.disable_error_logging) {
|
||||||
|
spdk_nvme_qpair_print_command(qpair, &req->cmd);
|
||||||
|
spdk_nvme_qpair_print_completion(qpair, cpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(cpl->cid == req->cmd.cid);
|
||||||
|
|
||||||
|
if (retry) {
|
||||||
|
req->retries++;
|
||||||
|
nvme_pcie_qpair_submit_tracker(qpair, tr);
|
||||||
|
} else {
|
||||||
|
TAILQ_REMOVE(&pqpair->outstanding_tr, tr, tq_list);
|
||||||
|
|
||||||
|
/* Only check admin requests from different processes. */
|
||||||
|
if (nvme_qpair_is_admin_queue(qpair) && req->pid != getpid()) {
|
||||||
|
req_from_current_proc = false;
|
||||||
|
nvme_pcie_qpair_insert_pending_admin_request(qpair, req, cpl);
|
||||||
|
} else {
|
||||||
|
nvme_complete_request(tr->cb_fn, tr->cb_arg, qpair, req, cpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req_from_current_proc == true) {
|
||||||
|
nvme_qpair_free_request(qpair, req);
|
||||||
|
}
|
||||||
|
|
||||||
|
tr->req = NULL;
|
||||||
|
|
||||||
|
TAILQ_INSERT_HEAD(&pqpair->free_tr, tr, tq_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nvme_pcie_qpair_manual_complete_tracker(struct spdk_nvme_qpair *qpair,
|
||||||
|
struct nvme_tracker *tr, uint32_t sct, uint32_t sc, uint32_t dnr,
|
||||||
|
bool print_on_error)
|
||||||
|
{
|
||||||
|
struct spdk_nvme_cpl cpl;
|
||||||
|
|
||||||
|
memset(&cpl, 0, sizeof(cpl));
|
||||||
|
cpl.sqid = qpair->id;
|
||||||
|
cpl.cid = tr->cid;
|
||||||
|
cpl.status.sct = sct;
|
||||||
|
cpl.status.sc = sc;
|
||||||
|
cpl.status.dnr = dnr;
|
||||||
|
nvme_pcie_qpair_complete_tracker(qpair, tr, &cpl, print_on_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nvme_pcie_qpair_abort_trackers(struct spdk_nvme_qpair *qpair, uint32_t dnr)
|
||||||
|
{
|
||||||
|
struct nvme_pcie_qpair *pqpair = nvme_pcie_qpair(qpair);
|
||||||
|
struct nvme_tracker *tr, *temp, *last;
|
||||||
|
|
||||||
|
last = TAILQ_LAST(&pqpair->outstanding_tr, nvme_outstanding_tr_head);
|
||||||
|
|
||||||
|
/* Abort previously submitted (outstanding) trs */
|
||||||
|
TAILQ_FOREACH_SAFE(tr, &pqpair->outstanding_tr, tq_list, temp) {
|
||||||
|
if (!qpair->ctrlr->opts.disable_error_logging) {
|
||||||
|
SPDK_ERRLOG("aborting outstanding command\n");
|
||||||
|
}
|
||||||
|
nvme_pcie_qpair_manual_complete_tracker(qpair, tr, SPDK_NVME_SCT_GENERIC,
|
||||||
|
SPDK_NVME_SC_ABORTED_BY_REQUEST, dnr, true);
|
||||||
|
|
||||||
|
if (tr == last) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nvme_pcie_admin_qpair_abort_aers(struct spdk_nvme_qpair *qpair)
|
||||||
|
{
|
||||||
|
struct nvme_pcie_qpair *pqpair = nvme_pcie_qpair(qpair);
|
||||||
|
struct nvme_tracker *tr;
|
||||||
|
|
||||||
|
tr = TAILQ_FIRST(&pqpair->outstanding_tr);
|
||||||
|
while (tr != NULL) {
|
||||||
|
assert(tr->req != NULL);
|
||||||
|
if (tr->req->cmd.opc == SPDK_NVME_OPC_ASYNC_EVENT_REQUEST) {
|
||||||
|
nvme_pcie_qpair_manual_complete_tracker(qpair, tr,
|
||||||
|
SPDK_NVME_SCT_GENERIC, SPDK_NVME_SC_ABORTED_SQ_DELETION, 0,
|
||||||
|
false);
|
||||||
|
tr = TAILQ_FIRST(&pqpair->outstanding_tr);
|
||||||
|
} else {
|
||||||
|
tr = TAILQ_NEXT(tr, tq_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nvme_pcie_admin_qpair_destroy(struct spdk_nvme_qpair *qpair)
|
||||||
|
{
|
||||||
|
nvme_pcie_admin_qpair_abort_aers(qpair);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nvme_pcie_qpair_abort_reqs(struct spdk_nvme_qpair *qpair, uint32_t dnr)
|
||||||
|
{
|
||||||
|
nvme_pcie_qpair_abort_trackers(qpair, dnr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nvme_pcie_qpair_check_timeout(struct spdk_nvme_qpair *qpair)
|
||||||
|
{
|
||||||
|
uint64_t t02;
|
||||||
|
struct nvme_tracker *tr, *tmp;
|
||||||
|
struct nvme_pcie_qpair *pqpair = nvme_pcie_qpair(qpair);
|
||||||
|
struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
|
||||||
|
struct spdk_nvme_ctrlr_process *active_proc;
|
||||||
|
|
||||||
|
/* Don't check timeouts during controller initialization. */
|
||||||
|
if (ctrlr->state != NVME_CTRLR_STATE_READY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nvme_qpair_is_admin_queue(qpair)) {
|
||||||
|
active_proc = nvme_ctrlr_get_current_process(ctrlr);
|
||||||
|
} else {
|
||||||
|
active_proc = qpair->active_proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only check timeouts if the current process has a timeout callback. */
|
||||||
|
if (active_proc == NULL || active_proc->timeout_cb_fn == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
t02 = spdk_get_ticks();
|
||||||
|
TAILQ_FOREACH_SAFE(tr, &pqpair->outstanding_tr, tq_list, tmp) {
|
||||||
|
assert(tr->req != NULL);
|
||||||
|
|
||||||
|
if (nvme_request_check_timeout(tr->req, tr->cid, active_proc, t02)) {
|
||||||
|
/*
|
||||||
|
* The requests are in order, so as soon as one has not timed out,
|
||||||
|
* stop iterating.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
nvme_pcie_qpair_process_completions(struct spdk_nvme_qpair *qpair, uint32_t max_completions)
|
||||||
|
{
|
||||||
|
struct nvme_pcie_qpair *pqpair = nvme_pcie_qpair(qpair);
|
||||||
|
struct nvme_tracker *tr;
|
||||||
|
struct spdk_nvme_cpl *cpl, *next_cpl;
|
||||||
|
uint32_t num_completions = 0;
|
||||||
|
struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
|
||||||
|
uint16_t next_cq_head;
|
||||||
|
uint8_t next_phase;
|
||||||
|
bool next_is_valid = false;
|
||||||
|
|
||||||
|
if (spdk_unlikely(nvme_qpair_is_admin_queue(qpair))) {
|
||||||
|
nvme_robust_mutex_lock(&ctrlr->ctrlr_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max_completions == 0 || max_completions > pqpair->max_completions_cap) {
|
||||||
|
/*
|
||||||
|
* max_completions == 0 means unlimited, but complete at most
|
||||||
|
* max_completions_cap batch of I/O at a time so that the completion
|
||||||
|
* queue doorbells don't wrap around.
|
||||||
|
*/
|
||||||
|
max_completions = pqpair->max_completions_cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
cpl = &pqpair->cpl[pqpair->cq_head];
|
||||||
|
|
||||||
|
if (!next_is_valid && cpl->status.p != pqpair->flags.phase) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spdk_likely(pqpair->cq_head + 1 != pqpair->num_entries)) {
|
||||||
|
next_cq_head = pqpair->cq_head + 1;
|
||||||
|
next_phase = pqpair->flags.phase;
|
||||||
|
} else {
|
||||||
|
next_cq_head = 0;
|
||||||
|
next_phase = !pqpair->flags.phase;
|
||||||
|
}
|
||||||
|
next_cpl = &pqpair->cpl[next_cq_head];
|
||||||
|
next_is_valid = (next_cpl->status.p == next_phase);
|
||||||
|
if (next_is_valid) {
|
||||||
|
__builtin_prefetch(&pqpair->tr[next_cpl->cid]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __PPC64__
|
||||||
|
/*
|
||||||
|
* This memory barrier prevents reordering of:
|
||||||
|
* - load after store from/to tr
|
||||||
|
* - load after load cpl phase and cpl cid
|
||||||
|
*/
|
||||||
|
spdk_mb();
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
__asm volatile("dmb oshld" ::: "memory");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (spdk_unlikely(++pqpair->cq_head == pqpair->num_entries)) {
|
||||||
|
pqpair->cq_head = 0;
|
||||||
|
pqpair->flags.phase = !pqpair->flags.phase;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr = &pqpair->tr[cpl->cid];
|
||||||
|
/* Prefetch the req's STAILQ_ENTRY since we'll need to access it
|
||||||
|
* as part of putting the req back on the qpair's free list.
|
||||||
|
*/
|
||||||
|
__builtin_prefetch(&tr->req->stailq);
|
||||||
|
pqpair->sq_head = cpl->sqhd;
|
||||||
|
|
||||||
|
if (tr->req) {
|
||||||
|
nvme_pcie_qpair_complete_tracker(qpair, tr, cpl, true);
|
||||||
|
} else {
|
||||||
|
SPDK_ERRLOG("cpl does not map to outstanding cmd\n");
|
||||||
|
spdk_nvme_qpair_print_completion(qpair, cpl);
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (++num_completions == max_completions) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_completions > 0) {
|
||||||
|
nvme_pcie_qpair_ring_cq_doorbell(qpair);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pqpair->flags.delay_cmd_submit) {
|
||||||
|
if (pqpair->last_sq_tail != pqpair->sq_tail) {
|
||||||
|
nvme_pcie_qpair_ring_sq_doorbell(qpair);
|
||||||
|
pqpair->last_sq_tail = pqpair->sq_tail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spdk_unlikely(ctrlr->timeout_enabled)) {
|
||||||
|
/*
|
||||||
|
* User registered for timeout callback
|
||||||
|
*/
|
||||||
|
nvme_pcie_qpair_check_timeout(qpair);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Before returning, complete any pending admin request. */
|
||||||
|
if (spdk_unlikely(nvme_qpair_is_admin_queue(qpair))) {
|
||||||
|
nvme_pcie_qpair_complete_pending_admin_request(qpair);
|
||||||
|
|
||||||
|
nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return num_completions;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nvme_pcie_qpair_destroy(struct spdk_nvme_qpair *qpair)
|
||||||
|
{
|
||||||
|
struct nvme_pcie_qpair *pqpair = nvme_pcie_qpair(qpair);
|
||||||
|
|
||||||
|
if (nvme_qpair_is_admin_queue(qpair)) {
|
||||||
|
nvme_pcie_admin_qpair_destroy(qpair);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* We check sq_vaddr and cq_vaddr to see if the user specified the memory
|
||||||
|
* buffers when creating the I/O queue.
|
||||||
|
* If the user specified them, we cannot free that memory.
|
||||||
|
* Nor do we free it if it's in the CMB.
|
||||||
|
*/
|
||||||
|
if (!pqpair->sq_vaddr && pqpair->cmd && !pqpair->sq_in_cmb) {
|
||||||
|
spdk_free(pqpair->cmd);
|
||||||
|
}
|
||||||
|
if (!pqpair->cq_vaddr && pqpair->cpl) {
|
||||||
|
spdk_free(pqpair->cpl);
|
||||||
|
}
|
||||||
|
if (pqpair->tr) {
|
||||||
|
spdk_free(pqpair->tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
nvme_qpair_deinit(qpair);
|
||||||
|
|
||||||
|
spdk_free(pqpair);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct spdk_nvme_qpair *
|
||||||
|
nvme_pcie_ctrlr_create_io_qpair(struct spdk_nvme_ctrlr *ctrlr, uint16_t qid,
|
||||||
|
const struct spdk_nvme_io_qpair_opts *opts)
|
||||||
|
{
|
||||||
|
struct nvme_pcie_qpair *pqpair;
|
||||||
|
struct spdk_nvme_qpair *qpair;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
assert(ctrlr != NULL);
|
||||||
|
|
||||||
|
pqpair = spdk_zmalloc(sizeof(*pqpair), 64, NULL,
|
||||||
|
SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_SHARE);
|
||||||
|
if (pqpair == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pqpair->num_entries = opts->io_queue_size;
|
||||||
|
pqpair->flags.delay_cmd_submit = opts->delay_cmd_submit;
|
||||||
|
|
||||||
|
qpair = &pqpair->qpair;
|
||||||
|
|
||||||
|
rc = nvme_qpair_init(qpair, qid, ctrlr, opts->qprio, opts->io_queue_requests);
|
||||||
|
if (rc != 0) {
|
||||||
|
nvme_pcie_qpair_destroy(qpair);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = nvme_pcie_qpair_construct(qpair, opts);
|
||||||
|
|
||||||
|
if (rc != 0) {
|
||||||
|
nvme_pcie_qpair_destroy(qpair);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return qpair;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nvme_pcie_ctrlr_delete_io_qpair(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair)
|
||||||
|
{
|
||||||
|
struct nvme_completion_poll_status *status;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
assert(ctrlr != NULL);
|
||||||
|
|
||||||
|
if (ctrlr->is_removed) {
|
||||||
|
goto free;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = calloc(1, sizeof(*status));
|
||||||
|
if (!status) {
|
||||||
|
SPDK_ERRLOG("Failed to allocate status tracker\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Delete the I/O submission queue */
|
||||||
|
rc = nvme_pcie_ctrlr_cmd_delete_io_sq(ctrlr, qpair, nvme_completion_poll_cb, status);
|
||||||
|
if (rc != 0) {
|
||||||
|
SPDK_ERRLOG("Failed to send request to delete_io_sq with rc=%d\n", rc);
|
||||||
|
free(status);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
if (nvme_wait_for_completion(ctrlr->adminq, status)) {
|
||||||
|
if (!status->timed_out) {
|
||||||
|
free(status);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now that the submission queue is deleted, the device is supposed to have
|
||||||
|
* completed any outstanding I/O. Try to complete them. If they don't complete,
|
||||||
|
* they'll be marked as aborted and completed below. */
|
||||||
|
nvme_pcie_qpair_process_completions(qpair, 0);
|
||||||
|
|
||||||
|
memset(status, 0, sizeof(*status));
|
||||||
|
/* Delete the completion queue */
|
||||||
|
rc = nvme_pcie_ctrlr_cmd_delete_io_cq(ctrlr, qpair, nvme_completion_poll_cb, status);
|
||||||
|
if (rc != 0) {
|
||||||
|
SPDK_ERRLOG("Failed to send request to delete_io_cq with rc=%d\n", rc);
|
||||||
|
free(status);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
if (nvme_wait_for_completion(ctrlr->adminq, status)) {
|
||||||
|
if (!status->timed_out) {
|
||||||
|
free(status);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
free(status);
|
||||||
|
|
||||||
|
free:
|
||||||
|
if (qpair->no_deletion_notification_needed == 0) {
|
||||||
|
/* Abort the rest of the I/O */
|
||||||
|
nvme_pcie_qpair_abort_trackers(qpair, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
nvme_pcie_qpair_destroy(qpair);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct spdk_nvme_transport_poll_group *
|
struct spdk_nvme_transport_poll_group *
|
||||||
nvme_pcie_poll_group_create(void)
|
nvme_pcie_poll_group_create(void)
|
||||||
{
|
{
|
||||||
|
@ -87,6 +87,8 @@ struct nvme_pcie_ctrlr {
|
|||||||
volatile uint32_t *doorbell_base;
|
volatile uint32_t *doorbell_base;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern __thread struct nvme_pcie_ctrlr *g_thread_mmio_ctrlr;
|
||||||
|
|
||||||
struct nvme_tracker {
|
struct nvme_tracker {
|
||||||
TAILQ_ENTRY(nvme_tracker) tq_list;
|
TAILQ_ENTRY(nvme_tracker) tq_list;
|
||||||
|
|
||||||
@ -203,6 +205,88 @@ nvme_pcie_ctrlr(struct spdk_nvme_ctrlr *ctrlr)
|
|||||||
return SPDK_CONTAINEROF(ctrlr, struct nvme_pcie_ctrlr, ctrlr);
|
return SPDK_CONTAINEROF(ctrlr, struct nvme_pcie_ctrlr, ctrlr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
nvme_pcie_qpair_need_event(uint16_t event_idx, uint16_t new_idx, uint16_t old)
|
||||||
|
{
|
||||||
|
return (uint16_t)(new_idx - event_idx) <= (uint16_t)(new_idx - old);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
nvme_pcie_qpair_update_mmio_required(struct spdk_nvme_qpair *qpair, uint16_t value,
|
||||||
|
volatile uint32_t *shadow_db,
|
||||||
|
volatile uint32_t *eventidx)
|
||||||
|
{
|
||||||
|
uint16_t old;
|
||||||
|
|
||||||
|
if (!shadow_db) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
old = *shadow_db;
|
||||||
|
*shadow_db = value;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure that the doorbell is updated before reading the EventIdx from
|
||||||
|
* memory
|
||||||
|
*/
|
||||||
|
spdk_mb();
|
||||||
|
|
||||||
|
if (!nvme_pcie_qpair_need_event(*eventidx, value, old)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
nvme_pcie_qpair_ring_sq_doorbell(struct spdk_nvme_qpair *qpair)
|
||||||
|
{
|
||||||
|
struct nvme_pcie_qpair *pqpair = nvme_pcie_qpair(qpair);
|
||||||
|
struct nvme_pcie_ctrlr *pctrlr = nvme_pcie_ctrlr(qpair->ctrlr);
|
||||||
|
bool need_mmio = true;
|
||||||
|
|
||||||
|
if (qpair->first_fused_submitted) {
|
||||||
|
/* This is first cmd of two fused commands - don't ring doorbell */
|
||||||
|
qpair->first_fused_submitted = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spdk_unlikely(pqpair->flags.has_shadow_doorbell)) {
|
||||||
|
need_mmio = nvme_pcie_qpair_update_mmio_required(qpair,
|
||||||
|
pqpair->sq_tail,
|
||||||
|
pqpair->shadow_doorbell.sq_tdbl,
|
||||||
|
pqpair->shadow_doorbell.sq_eventidx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spdk_likely(need_mmio)) {
|
||||||
|
spdk_wmb();
|
||||||
|
g_thread_mmio_ctrlr = pctrlr;
|
||||||
|
spdk_mmio_write_4(pqpair->sq_tdbl, pqpair->sq_tail);
|
||||||
|
g_thread_mmio_ctrlr = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
nvme_pcie_qpair_ring_cq_doorbell(struct spdk_nvme_qpair *qpair)
|
||||||
|
{
|
||||||
|
struct nvme_pcie_qpair *pqpair = nvme_pcie_qpair(qpair);
|
||||||
|
struct nvme_pcie_ctrlr *pctrlr = nvme_pcie_ctrlr(qpair->ctrlr);
|
||||||
|
bool need_mmio = true;
|
||||||
|
|
||||||
|
if (spdk_unlikely(pqpair->flags.has_shadow_doorbell)) {
|
||||||
|
need_mmio = nvme_pcie_qpair_update_mmio_required(qpair,
|
||||||
|
pqpair->cq_head,
|
||||||
|
pqpair->shadow_doorbell.cq_hdbl,
|
||||||
|
pqpair->shadow_doorbell.cq_eventidx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spdk_likely(need_mmio)) {
|
||||||
|
g_thread_mmio_ctrlr = pctrlr;
|
||||||
|
spdk_mmio_write_4(pqpair->cq_hdbl, pqpair->cq_head);
|
||||||
|
g_thread_mmio_ctrlr = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int nvme_pcie_qpair_reset(struct spdk_nvme_qpair *qpair);
|
int nvme_pcie_qpair_reset(struct spdk_nvme_qpair *qpair);
|
||||||
int nvme_pcie_qpair_construct(struct spdk_nvme_qpair *qpair,
|
int nvme_pcie_qpair_construct(struct spdk_nvme_qpair *qpair,
|
||||||
const struct spdk_nvme_io_qpair_opts *opts);
|
const struct spdk_nvme_io_qpair_opts *opts);
|
||||||
@ -221,6 +305,22 @@ int nvme_pcie_ctrlr_cmd_delete_io_sq(struct spdk_nvme_ctrlr *ctrlr, struct spdk_
|
|||||||
spdk_nvme_cmd_cb cb_fn, void *cb_arg);
|
spdk_nvme_cmd_cb cb_fn, void *cb_arg);
|
||||||
int nvme_pcie_ctrlr_connect_qpair(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair);
|
int nvme_pcie_ctrlr_connect_qpair(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair);
|
||||||
void nvme_pcie_ctrlr_disconnect_qpair(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair);
|
void nvme_pcie_ctrlr_disconnect_qpair(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair);
|
||||||
|
void nvme_pcie_qpair_abort_trackers(struct spdk_nvme_qpair *qpair, uint32_t dnr);
|
||||||
|
void nvme_pcie_qpair_manual_complete_tracker(struct spdk_nvme_qpair *qpair,
|
||||||
|
struct nvme_tracker *tr, uint32_t sct, uint32_t sc, uint32_t dnr,
|
||||||
|
bool print_on_error);
|
||||||
|
void nvme_pcie_qpair_complete_tracker(struct spdk_nvme_qpair *qpair, struct nvme_tracker *tr,
|
||||||
|
struct spdk_nvme_cpl *cpl, bool print_on_error);
|
||||||
|
void nvme_pcie_qpair_submit_tracker(struct spdk_nvme_qpair *qpair, struct nvme_tracker *tr);
|
||||||
|
void nvme_pcie_admin_qpair_abort_aers(struct spdk_nvme_qpair *qpair);
|
||||||
|
void nvme_pcie_admin_qpair_destroy(struct spdk_nvme_qpair *qpair);
|
||||||
|
void nvme_pcie_qpair_abort_reqs(struct spdk_nvme_qpair *qpair, uint32_t dnr);
|
||||||
|
int32_t nvme_pcie_qpair_process_completions(struct spdk_nvme_qpair *qpair,
|
||||||
|
uint32_t max_completions);
|
||||||
|
int nvme_pcie_qpair_destroy(struct spdk_nvme_qpair *qpair);
|
||||||
|
struct spdk_nvme_qpair *nvme_pcie_ctrlr_create_io_qpair(struct spdk_nvme_ctrlr *ctrlr, uint16_t qid,
|
||||||
|
const struct spdk_nvme_io_qpair_opts *opts);
|
||||||
|
int nvme_pcie_ctrlr_delete_io_qpair(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair);
|
||||||
|
|
||||||
struct spdk_nvme_transport_poll_group *nvme_pcie_poll_group_create(void);
|
struct spdk_nvme_transport_poll_group *nvme_pcie_poll_group_create(void);
|
||||||
int nvme_pcie_poll_group_connect_qpair(struct spdk_nvme_qpair *qpair);
|
int nvme_pcie_poll_group_connect_qpair(struct spdk_nvme_qpair *qpair);
|
||||||
|
@ -354,16 +354,13 @@ nvme_vfio_ctrlr_enable(struct spdk_nvme_ctrlr *ctrlr)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
nvme_vfio_qpair_destroy(struct spdk_nvme_qpair *qpair);
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
nvme_vfio_ctrlr_destruct(struct spdk_nvme_ctrlr *ctrlr)
|
nvme_vfio_ctrlr_destruct(struct spdk_nvme_ctrlr *ctrlr)
|
||||||
{
|
{
|
||||||
struct nvme_vfio_ctrlr *vctrlr = nvme_vfio_ctrlr(ctrlr);
|
struct nvme_vfio_ctrlr *vctrlr = nvme_vfio_ctrlr(ctrlr);
|
||||||
|
|
||||||
if (ctrlr->adminq) {
|
if (ctrlr->adminq) {
|
||||||
nvme_vfio_qpair_destroy(ctrlr->adminq);
|
nvme_pcie_qpair_destroy(ctrlr->adminq);
|
||||||
}
|
}
|
||||||
|
|
||||||
nvme_ctrlr_destruct_finish(ctrlr);
|
nvme_ctrlr_destruct_finish(ctrlr);
|
||||||
@ -389,294 +386,6 @@ nvme_vfio_ctrlr_get_max_sges(struct spdk_nvme_ctrlr *ctrlr)
|
|||||||
return NVME_MAX_SGES;
|
return NVME_MAX_SGES;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct spdk_nvme_qpair *
|
|
||||||
nvme_vfio_ctrlr_create_io_qpair(struct spdk_nvme_ctrlr *ctrlr, uint16_t qid,
|
|
||||||
const struct spdk_nvme_io_qpair_opts *opts)
|
|
||||||
{
|
|
||||||
struct nvme_pcie_qpair *vqpair;
|
|
||||||
struct spdk_nvme_qpair *qpair;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
assert(ctrlr != NULL);
|
|
||||||
|
|
||||||
vqpair = spdk_zmalloc(sizeof(*vqpair), 64, NULL,
|
|
||||||
SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_SHARE);
|
|
||||||
if (vqpair == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
vqpair->num_entries = opts->io_queue_size;
|
|
||||||
vqpair->flags.delay_cmd_submit = opts->delay_cmd_submit;
|
|
||||||
|
|
||||||
qpair = &vqpair->qpair;
|
|
||||||
|
|
||||||
rc = nvme_qpair_init(qpair, qid, ctrlr, opts->qprio, opts->io_queue_requests);
|
|
||||||
if (rc != 0) {
|
|
||||||
nvme_vfio_qpair_destroy(qpair);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = nvme_pcie_qpair_construct(qpair, opts);
|
|
||||||
|
|
||||||
if (rc != 0) {
|
|
||||||
nvme_vfio_qpair_destroy(qpair);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return qpair;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
nvme_vfio_qpair_abort_trackers(struct spdk_nvme_qpair *qpair, uint32_t dnr);
|
|
||||||
|
|
||||||
static int
|
|
||||||
nvme_vfio_ctrlr_delete_io_qpair(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair)
|
|
||||||
{
|
|
||||||
struct nvme_completion_poll_status *status;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
assert(ctrlr != NULL);
|
|
||||||
|
|
||||||
if (ctrlr->is_removed) {
|
|
||||||
goto free;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = calloc(1, sizeof(*status));
|
|
||||||
if (!status) {
|
|
||||||
SPDK_ERRLOG("Failed to allocate status tracker\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Delete the I/O submission queue */
|
|
||||||
rc = nvme_pcie_ctrlr_cmd_delete_io_sq(ctrlr, qpair, nvme_completion_poll_cb, status);
|
|
||||||
if (rc != 0) {
|
|
||||||
SPDK_ERRLOG("Failed to send request to delete_io_sq with rc=%d\n", rc);
|
|
||||||
free(status);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
if (nvme_wait_for_completion(ctrlr->adminq, status)) {
|
|
||||||
if (!status->timed_out) {
|
|
||||||
free(status);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(status, 0, sizeof(*status));
|
|
||||||
/* Delete the completion queue */
|
|
||||||
rc = nvme_pcie_ctrlr_cmd_delete_io_cq(ctrlr, qpair, nvme_completion_poll_cb, status);
|
|
||||||
if (rc != 0) {
|
|
||||||
SPDK_ERRLOG("Failed to send request to delete_io_cq with rc=%d\n", rc);
|
|
||||||
free(status);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
if (nvme_wait_for_completion(ctrlr->adminq, status)) {
|
|
||||||
if (!status->timed_out) {
|
|
||||||
free(status);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
free(status);
|
|
||||||
|
|
||||||
free:
|
|
||||||
if (qpair->no_deletion_notification_needed == 0) {
|
|
||||||
/* Abort the rest of the I/O */
|
|
||||||
nvme_vfio_qpair_abort_trackers(qpair, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
nvme_vfio_qpair_destroy(qpair);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
nvme_vfio_qpair_ring_sq_doorbell(struct spdk_nvme_qpair *qpair)
|
|
||||||
{
|
|
||||||
struct nvme_pcie_qpair *vqpair = nvme_pcie_qpair(qpair);
|
|
||||||
|
|
||||||
if (qpair->first_fused_submitted) {
|
|
||||||
/* This is first cmd of two fused commands - don't ring doorbell */
|
|
||||||
qpair->first_fused_submitted = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
spdk_wmb();
|
|
||||||
spdk_mmio_write_4(vqpair->sq_tdbl, vqpair->sq_tail);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
nvme_vfio_qpair_ring_cq_doorbell(struct spdk_nvme_qpair *qpair)
|
|
||||||
{
|
|
||||||
struct nvme_pcie_qpair *vqpair = nvme_pcie_qpair(qpair);
|
|
||||||
|
|
||||||
spdk_mmio_write_4(vqpair->cq_hdbl, vqpair->cq_head);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
nvme_vfio_qpair_submit_tracker(struct spdk_nvme_qpair *qpair, struct nvme_tracker *tr)
|
|
||||||
{
|
|
||||||
struct nvme_request *req;
|
|
||||||
struct nvme_pcie_qpair *vqpair = nvme_pcie_qpair(qpair);
|
|
||||||
|
|
||||||
req = tr->req;
|
|
||||||
assert(req != NULL);
|
|
||||||
|
|
||||||
if (req->cmd.fuse == SPDK_NVME_IO_FLAGS_FUSE_FIRST) {
|
|
||||||
/* This is first cmd of two fused commands - don't ring doorbell */
|
|
||||||
qpair->first_fused_submitted = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
vqpair->cmd[vqpair->sq_tail] = req->cmd;
|
|
||||||
|
|
||||||
if (spdk_unlikely(++vqpair->sq_tail == vqpair->num_entries)) {
|
|
||||||
vqpair->sq_tail = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spdk_unlikely(vqpair->sq_tail == vqpair->sq_head)) {
|
|
||||||
SPDK_ERRLOG("sq_tail is passing sq_head!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
nvme_vfio_qpair_ring_sq_doorbell(qpair);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
nvme_vfio_qpair_complete_tracker(struct spdk_nvme_qpair *qpair, struct nvme_tracker *tr,
|
|
||||||
struct spdk_nvme_cpl *cpl, bool print_on_error)
|
|
||||||
{
|
|
||||||
struct nvme_pcie_qpair *vqpair = nvme_pcie_qpair(qpair);
|
|
||||||
struct nvme_request *req;
|
|
||||||
bool retry, error;
|
|
||||||
bool req_from_current_proc = true;
|
|
||||||
|
|
||||||
req = tr->req;
|
|
||||||
|
|
||||||
assert(req != NULL);
|
|
||||||
|
|
||||||
error = spdk_nvme_cpl_is_error(cpl);
|
|
||||||
retry = error && nvme_completion_is_retry(cpl) &&
|
|
||||||
req->retries < vqpair->retry_count;
|
|
||||||
|
|
||||||
if (error && print_on_error && !qpair->ctrlr->opts.disable_error_logging) {
|
|
||||||
spdk_nvme_qpair_print_command(qpair, &req->cmd);
|
|
||||||
spdk_nvme_qpair_print_completion(qpair, cpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(cpl->cid == req->cmd.cid);
|
|
||||||
|
|
||||||
if (retry) {
|
|
||||||
req->retries++;
|
|
||||||
nvme_vfio_qpair_submit_tracker(qpair, tr);
|
|
||||||
} else {
|
|
||||||
/* Only check admin requests from different processes. */
|
|
||||||
if (nvme_qpair_is_admin_queue(qpair) && req->pid != getpid()) {
|
|
||||||
req_from_current_proc = false;
|
|
||||||
nvme_pcie_qpair_insert_pending_admin_request(qpair, req, cpl);
|
|
||||||
} else {
|
|
||||||
nvme_complete_request(tr->cb_fn, tr->cb_arg, qpair, req, cpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req_from_current_proc == true) {
|
|
||||||
nvme_qpair_free_request(qpair, req);
|
|
||||||
}
|
|
||||||
|
|
||||||
tr->req = NULL;
|
|
||||||
|
|
||||||
TAILQ_REMOVE(&vqpair->outstanding_tr, tr, tq_list);
|
|
||||||
TAILQ_INSERT_HEAD(&vqpair->free_tr, tr, tq_list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
nvme_vfio_qpair_manual_complete_tracker(struct spdk_nvme_qpair *qpair,
|
|
||||||
struct nvme_tracker *tr, uint32_t sct, uint32_t sc, uint32_t dnr,
|
|
||||||
bool print_on_error)
|
|
||||||
{
|
|
||||||
struct spdk_nvme_cpl cpl;
|
|
||||||
|
|
||||||
memset(&cpl, 0, sizeof(cpl));
|
|
||||||
cpl.sqid = qpair->id;
|
|
||||||
cpl.cid = tr->cid;
|
|
||||||
cpl.status.sct = sct;
|
|
||||||
cpl.status.sc = sc;
|
|
||||||
cpl.status.dnr = dnr;
|
|
||||||
nvme_vfio_qpair_complete_tracker(qpair, tr, &cpl, print_on_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
nvme_vfio_qpair_abort_trackers(struct spdk_nvme_qpair *qpair, uint32_t dnr)
|
|
||||||
{
|
|
||||||
struct nvme_pcie_qpair *pqpair = nvme_pcie_qpair(qpair);
|
|
||||||
struct nvme_tracker *tr, *temp, *last;
|
|
||||||
|
|
||||||
last = TAILQ_LAST(&pqpair->outstanding_tr, nvme_outstanding_tr_head);
|
|
||||||
|
|
||||||
/* Abort previously submitted (outstanding) trs */
|
|
||||||
TAILQ_FOREACH_SAFE(tr, &pqpair->outstanding_tr, tq_list, temp) {
|
|
||||||
if (!qpair->ctrlr->opts.disable_error_logging) {
|
|
||||||
SPDK_ERRLOG("aborting outstanding command\n");
|
|
||||||
}
|
|
||||||
nvme_vfio_qpair_manual_complete_tracker(qpair, tr, SPDK_NVME_SCT_GENERIC,
|
|
||||||
SPDK_NVME_SC_ABORTED_BY_REQUEST, dnr, true);
|
|
||||||
|
|
||||||
if (tr == last) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
nvme_vfio_qpair_abort_reqs(struct spdk_nvme_qpair *qpair, uint32_t dnr)
|
|
||||||
{
|
|
||||||
nvme_vfio_qpair_abort_trackers(qpair, dnr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
nvme_vfio_admin_qpair_abort_aers(struct spdk_nvme_qpair *qpair)
|
|
||||||
{
|
|
||||||
struct nvme_pcie_qpair *vqpair = nvme_pcie_qpair(qpair);
|
|
||||||
struct nvme_tracker *tr;
|
|
||||||
|
|
||||||
tr = TAILQ_FIRST(&vqpair->outstanding_tr);
|
|
||||||
while (tr != NULL) {
|
|
||||||
assert(tr->req != NULL);
|
|
||||||
if (tr->req->cmd.opc == SPDK_NVME_OPC_ASYNC_EVENT_REQUEST) {
|
|
||||||
nvme_vfio_qpair_manual_complete_tracker(qpair, tr,
|
|
||||||
SPDK_NVME_SCT_GENERIC, SPDK_NVME_SC_ABORTED_SQ_DELETION, 0,
|
|
||||||
false);
|
|
||||||
tr = TAILQ_FIRST(&vqpair->outstanding_tr);
|
|
||||||
} else {
|
|
||||||
tr = TAILQ_NEXT(tr, tq_list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
nvme_vfio_admin_qpair_destroy(struct spdk_nvme_qpair *qpair)
|
|
||||||
{
|
|
||||||
nvme_vfio_admin_qpair_abort_aers(qpair);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
nvme_vfio_qpair_destroy(struct spdk_nvme_qpair *qpair)
|
|
||||||
{
|
|
||||||
struct nvme_pcie_qpair *vqpair = nvme_pcie_qpair(qpair);
|
|
||||||
|
|
||||||
if (nvme_qpair_is_admin_queue(qpair)) {
|
|
||||||
nvme_vfio_admin_qpair_destroy(qpair);
|
|
||||||
}
|
|
||||||
|
|
||||||
spdk_free(vqpair->cmd);
|
|
||||||
spdk_free(vqpair->cpl);
|
|
||||||
|
|
||||||
if (vqpair->tr) {
|
|
||||||
spdk_free(vqpair->tr);
|
|
||||||
}
|
|
||||||
|
|
||||||
nvme_qpair_deinit(qpair);
|
|
||||||
|
|
||||||
spdk_free(vqpair);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
nvme_vfio_prp_list_append(struct nvme_tracker *tr, uint32_t *prp_index, void *virt_addr, size_t len,
|
nvme_vfio_prp_list_append(struct nvme_tracker *tr, uint32_t *prp_index, void *virt_addr, size_t len,
|
||||||
uint32_t page_size)
|
uint32_t page_size)
|
||||||
@ -755,7 +464,7 @@ nvme_vfio_qpair_build_contig_request(struct spdk_nvme_qpair *qpair, struct nvme_
|
|||||||
rc = nvme_vfio_prp_list_append(tr, &prp_index, req->payload.contig_or_cb_arg + req->payload_offset,
|
rc = nvme_vfio_prp_list_append(tr, &prp_index, req->payload.contig_or_cb_arg + req->payload_offset,
|
||||||
req->payload_size, qpair->ctrlr->page_size);
|
req->payload_size, qpair->ctrlr->page_size);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
nvme_vfio_qpair_manual_complete_tracker(qpair, tr, SPDK_NVME_SCT_GENERIC,
|
nvme_pcie_qpair_manual_complete_tracker(qpair, tr, SPDK_NVME_SCT_GENERIC,
|
||||||
SPDK_NVME_SC_INVALID_FIELD,
|
SPDK_NVME_SC_INVALID_FIELD,
|
||||||
1 /* do not retry */, true);
|
1 /* do not retry */, true);
|
||||||
}
|
}
|
||||||
@ -797,7 +506,7 @@ nvme_vfio_qpair_submit_request(struct spdk_nvme_qpair *qpair, struct nvme_reques
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nvme_vfio_qpair_submit_tracker(qpair, tr);
|
nvme_pcie_qpair_submit_tracker(qpair, tr);
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
if (spdk_unlikely(nvme_qpair_is_admin_queue(qpair))) {
|
if (spdk_unlikely(nvme_qpair_is_admin_queue(qpair))) {
|
||||||
@ -807,97 +516,6 @@ exit:
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t
|
|
||||||
nvme_vfio_qpair_process_completions(struct spdk_nvme_qpair *qpair, uint32_t max_completions)
|
|
||||||
{
|
|
||||||
struct nvme_pcie_qpair *vqpair = nvme_pcie_qpair(qpair);
|
|
||||||
struct nvme_tracker *tr;
|
|
||||||
struct spdk_nvme_cpl *cpl, *next_cpl;
|
|
||||||
uint32_t num_completions = 0;
|
|
||||||
struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
|
|
||||||
uint16_t next_cq_head;
|
|
||||||
uint8_t next_phase;
|
|
||||||
bool next_is_valid = false;
|
|
||||||
|
|
||||||
if (spdk_unlikely(nvme_qpair_is_admin_queue(qpair))) {
|
|
||||||
nvme_robust_mutex_lock(&ctrlr->ctrlr_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (max_completions == 0 || max_completions > vqpair->max_completions_cap) {
|
|
||||||
/*
|
|
||||||
* max_completions == 0 means unlimited, but complete at most
|
|
||||||
* max_completions_cap batch of I/O at a time so that the completion
|
|
||||||
* queue doorbells don't wrap around.
|
|
||||||
*/
|
|
||||||
max_completions = vqpair->max_completions_cap;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
cpl = &vqpair->cpl[vqpair->cq_head];
|
|
||||||
|
|
||||||
if (!next_is_valid && cpl->status.p != vqpair->flags.phase) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spdk_likely(vqpair->cq_head + 1 != vqpair->num_entries)) {
|
|
||||||
next_cq_head = vqpair->cq_head + 1;
|
|
||||||
next_phase = vqpair->flags.phase;
|
|
||||||
} else {
|
|
||||||
next_cq_head = 0;
|
|
||||||
next_phase = !vqpair->flags.phase;
|
|
||||||
}
|
|
||||||
next_cpl = &vqpair->cpl[next_cq_head];
|
|
||||||
next_is_valid = (next_cpl->status.p == next_phase);
|
|
||||||
if (next_is_valid) {
|
|
||||||
__builtin_prefetch(&vqpair->tr[next_cpl->cid]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spdk_unlikely(++vqpair->cq_head == vqpair->num_entries)) {
|
|
||||||
vqpair->cq_head = 0;
|
|
||||||
vqpair->flags.phase = !vqpair->flags.phase;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr = &vqpair->tr[cpl->cid];
|
|
||||||
/* Prefetch the req's STAILQ_ENTRY since we'll need to access it
|
|
||||||
* as part of putting the req back on the qpair's free list.
|
|
||||||
*/
|
|
||||||
__builtin_prefetch(&tr->req->stailq);
|
|
||||||
vqpair->sq_head = cpl->sqhd;
|
|
||||||
|
|
||||||
if (tr->req) {
|
|
||||||
nvme_vfio_qpair_complete_tracker(qpair, tr, cpl, true);
|
|
||||||
} else {
|
|
||||||
SPDK_ERRLOG("cpl does not map to outstanding cmd\n");
|
|
||||||
spdk_nvme_qpair_print_completion(qpair, cpl);
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (++num_completions == max_completions) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (num_completions > 0) {
|
|
||||||
nvme_vfio_qpair_ring_cq_doorbell(qpair);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vqpair->flags.delay_cmd_submit) {
|
|
||||||
if (vqpair->last_sq_tail != vqpair->sq_tail) {
|
|
||||||
nvme_vfio_qpair_ring_sq_doorbell(qpair);
|
|
||||||
vqpair->last_sq_tail = vqpair->sq_tail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Before returning, complete any pending admin request. */
|
|
||||||
if (spdk_unlikely(nvme_qpair_is_admin_queue(qpair))) {
|
|
||||||
nvme_pcie_qpair_complete_pending_admin_request(qpair);
|
|
||||||
|
|
||||||
nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
return num_completions;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct spdk_nvme_transport_ops vfio_ops = {
|
const struct spdk_nvme_transport_ops vfio_ops = {
|
||||||
.name = "VFIOUSER",
|
.name = "VFIOUSER",
|
||||||
.type = SPDK_NVME_TRANSPORT_VFIOUSER,
|
.type = SPDK_NVME_TRANSPORT_VFIOUSER,
|
||||||
@ -914,16 +532,16 @@ const struct spdk_nvme_transport_ops vfio_ops = {
|
|||||||
.ctrlr_get_max_xfer_size = nvme_vfio_ctrlr_get_max_xfer_size,
|
.ctrlr_get_max_xfer_size = nvme_vfio_ctrlr_get_max_xfer_size,
|
||||||
.ctrlr_get_max_sges = nvme_vfio_ctrlr_get_max_sges,
|
.ctrlr_get_max_sges = nvme_vfio_ctrlr_get_max_sges,
|
||||||
|
|
||||||
.ctrlr_create_io_qpair = nvme_vfio_ctrlr_create_io_qpair,
|
.ctrlr_create_io_qpair = nvme_pcie_ctrlr_create_io_qpair,
|
||||||
.ctrlr_delete_io_qpair = nvme_vfio_ctrlr_delete_io_qpair,
|
.ctrlr_delete_io_qpair = nvme_pcie_ctrlr_delete_io_qpair,
|
||||||
.ctrlr_connect_qpair = nvme_pcie_ctrlr_connect_qpair,
|
.ctrlr_connect_qpair = nvme_pcie_ctrlr_connect_qpair,
|
||||||
.ctrlr_disconnect_qpair = nvme_pcie_ctrlr_disconnect_qpair,
|
.ctrlr_disconnect_qpair = nvme_pcie_ctrlr_disconnect_qpair,
|
||||||
.admin_qpair_abort_aers = nvme_vfio_admin_qpair_abort_aers,
|
.admin_qpair_abort_aers = nvme_pcie_admin_qpair_abort_aers,
|
||||||
|
|
||||||
.qpair_reset = nvme_pcie_qpair_reset,
|
.qpair_reset = nvme_pcie_qpair_reset,
|
||||||
.qpair_abort_reqs = nvme_vfio_qpair_abort_reqs,
|
.qpair_abort_reqs = nvme_pcie_qpair_abort_reqs,
|
||||||
.qpair_submit_request = nvme_vfio_qpair_submit_request,
|
.qpair_submit_request = nvme_vfio_qpair_submit_request,
|
||||||
.qpair_process_completions = nvme_vfio_qpair_process_completions,
|
.qpair_process_completions = nvme_pcie_qpair_process_completions,
|
||||||
|
|
||||||
.poll_group_create = nvme_pcie_poll_group_create,
|
.poll_group_create = nvme_pcie_poll_group_create,
|
||||||
.poll_group_connect_qpair = nvme_pcie_poll_group_connect_qpair,
|
.poll_group_connect_qpair = nvme_pcie_poll_group_connect_qpair,
|
||||||
|
Loading…
Reference in New Issue
Block a user