diff --git a/lib/nvme/nvme_internal.h b/lib/nvme/nvme_internal.h index 2b3cc0ca1..7cdf2ad78 100644 --- a/lib/nvme/nvme_internal.h +++ b/lib/nvme/nvme_internal.h @@ -439,6 +439,8 @@ struct spdk_nvme_qpair { enum spdk_nvme_transport_type trtype; + uint32_t num_outstanding_reqs; + /* request object used only for this qpair's FABRICS/CONNECT command (if needed) */ struct nvme_request *reserved_req; @@ -1268,6 +1270,7 @@ nvme_allocate_request(struct spdk_nvme_qpair *qpair, } STAILQ_REMOVE_HEAD(&qpair->free_req, stailq); + qpair->num_outstanding_reqs++; /* * Only memset/zero fields that need it. All other fields @@ -1357,6 +1360,9 @@ nvme_free_request(struct nvme_request *req) */ if (spdk_likely(req->qpair->reserved_req != req)) { STAILQ_INSERT_HEAD(&req->qpair->free_req, req, stailq); + + assert(req->qpair->num_outstanding_reqs > 0); + req->qpair->num_outstanding_reqs--; } } @@ -1381,6 +1387,9 @@ nvme_qpair_free_request(struct spdk_nvme_qpair *qpair, struct nvme_request *req) assert(req->num_children == 0); STAILQ_INSERT_HEAD(&qpair->free_req, req, stailq); + + assert(req->qpair->num_outstanding_reqs > 0); + req->qpair->num_outstanding_reqs--; } static inline void diff --git a/lib/nvme/nvme_qpair.c b/lib/nvme/nvme_qpair.c index 43e8a40c0..e79e00b9c 100644 --- a/lib/nvme/nvme_qpair.c +++ b/lib/nvme/nvme_qpair.c @@ -851,6 +851,7 @@ nvme_qpair_init(struct spdk_nvme_qpair *qpair, uint16_t id, qpair->is_new_qpair = true; qpair->async = async; qpair->poll_status = NULL; + qpair->num_outstanding_reqs = 0; STAILQ_INIT(&qpair->free_req); STAILQ_INIT(&qpair->queued_req); diff --git a/test/unit/lib/nvme/nvme.c/nvme_ut.c b/test/unit/lib/nvme/nvme.c/nvme_ut.c index bd6cf3644..a14e3c52f 100644 --- a/test/unit/lib/nvme/nvme.c/nvme_ut.c +++ b/test/unit/lib/nvme/nvme.c/nvme_ut.c @@ -688,11 +688,13 @@ test_nvme_allocate_request(void) memset(&payload, 0x5a, payload_struct_size); STAILQ_INIT(&qpair.free_req); STAILQ_INIT(&qpair.queued_req); + qpair.num_outstanding_reqs = 0; /* Test trying to allocate a request when no requests are available */ req = nvme_allocate_request(&qpair, &payload, payload_struct_size, 0, cb_fn, cb_arg); CU_ASSERT(req == NULL); + CU_ASSERT(qpair.num_outstanding_reqs == 0); /* put a dummy on the queue, and then allocate one */ STAILQ_INSERT_HEAD(&qpair.free_req, &dummy_req, stailq); @@ -701,6 +703,7 @@ test_nvme_allocate_request(void) /* all the req elements should now match the passed in parameters */ SPDK_CU_ASSERT_FATAL(req != NULL); + CU_ASSERT(qpair.num_outstanding_reqs == 1); CU_ASSERT(req->cb_fn == cb_fn); CU_ASSERT(req->cb_arg == cb_arg); CU_ASSERT(memcmp(&req->payload, &payload, payload_struct_size) == 0); @@ -718,6 +721,7 @@ test_nvme_free_request(void) /* put a req on the Q, take it off and compare */ memset(&match_req.cmd, 0x5a, sizeof(struct spdk_nvme_cmd)); match_req.qpair = &qpair; + qpair.num_outstanding_reqs = 1; /* the code under tests asserts this condition */ match_req.num_children = 0; STAILQ_INIT(&qpair.free_req); @@ -726,6 +730,7 @@ test_nvme_free_request(void) nvme_free_request(&match_req); req = STAILQ_FIRST(&match_req.qpair->free_req); CU_ASSERT(req == &match_req); + CU_ASSERT(qpair.num_outstanding_reqs == 0); } static void diff --git a/test/unit/lib/nvme/nvme_qpair.c/nvme_qpair_ut.c b/test/unit/lib/nvme/nvme_qpair.c/nvme_qpair_ut.c index a5cf6dbe2..53cc03101 100644 --- a/test/unit/lib/nvme/nvme_qpair.c/nvme_qpair_ut.c +++ b/test/unit/lib/nvme/nvme_qpair.c/nvme_qpair_ut.c @@ -176,6 +176,7 @@ test_nvme_qpair_process_completions(void) STAILQ_INIT(&qpair.queued_req); STAILQ_INSERT_TAIL(&qpair.queued_req, &dummy_1, stailq); STAILQ_INSERT_TAIL(&qpair.queued_req, &dummy_2, stailq); + qpair.num_outstanding_reqs = 2; /* If the controller is failed, return -ENXIO */ ctrlr.is_failed = true; @@ -185,6 +186,7 @@ test_nvme_qpair_process_completions(void) CU_ASSERT(!STAILQ_EMPTY(&qpair.queued_req)); CU_ASSERT(g_num_cb_passed == 0); CU_ASSERT(g_num_cb_failed == 0); + CU_ASSERT(qpair.num_outstanding_reqs == 2); /* Same if the qpair is failed at the transport layer. */ ctrlr.is_failed = false; @@ -195,6 +197,7 @@ test_nvme_qpair_process_completions(void) CU_ASSERT(!STAILQ_EMPTY(&qpair.queued_req)); CU_ASSERT(g_num_cb_passed == 0); CU_ASSERT(g_num_cb_failed == 0); + CU_ASSERT(qpair.num_outstanding_reqs == 2); /* If the controller is removed, make sure we abort the requests. */ ctrlr.is_failed = true; @@ -205,6 +208,7 @@ test_nvme_qpair_process_completions(void) CU_ASSERT(STAILQ_EMPTY(&qpair.queued_req)); CU_ASSERT(g_num_cb_passed == 0); CU_ASSERT(g_num_cb_failed == 2); + CU_ASSERT(qpair.num_outstanding_reqs == 0); /* If we are resetting, make sure that we don't call into the transport. */ STAILQ_INSERT_TAIL(&qpair.queued_req, &dummy_1, stailq); @@ -626,10 +630,12 @@ test_nvme_qpair_manual_complete_request(void) qpair.ctrlr->opts.disable_error_logging = false; STAILQ_INIT(&qpair.free_req); SPDK_CU_ASSERT_FATAL(STAILQ_EMPTY(&qpair.free_req)); + qpair.num_outstanding_reqs = 1; nvme_qpair_manual_complete_request(&qpair, &req, SPDK_NVME_SCT_GENERIC, SPDK_NVME_SC_SUCCESS, 1, true); CU_ASSERT(!STAILQ_EMPTY(&qpair.free_req)); + CU_ASSERT(qpair.num_outstanding_reqs == 0); } static void @@ -688,12 +694,14 @@ test_nvme_qpair_init_deinit(void) STAILQ_REMOVE(&qpair.free_req, reqs[2], nvme_request, stailq); STAILQ_INSERT_TAIL(&qpair.err_req_head, reqs[2], stailq); CU_ASSERT(STAILQ_EMPTY(&qpair.free_req)); + qpair.num_outstanding_reqs = 3; nvme_qpair_deinit(&qpair); CU_ASSERT(STAILQ_EMPTY(&qpair.queued_req)); CU_ASSERT(STAILQ_EMPTY(&qpair.aborting_queued_req)); CU_ASSERT(STAILQ_EMPTY(&qpair.err_req_head)); CU_ASSERT(TAILQ_EMPTY(&qpair.err_cmd_head)); + CU_ASSERT(qpair.num_outstanding_reqs == 0); } static void diff --git a/test/unit/lib/nvme/nvme_tcp.c/nvme_tcp_ut.c b/test/unit/lib/nvme/nvme_tcp.c/nvme_tcp_ut.c index 2052a5af4..434e10fcf 100644 --- a/test/unit/lib/nvme/nvme_tcp.c/nvme_tcp_ut.c +++ b/test/unit/lib/nvme/nvme_tcp.c/nvme_tcp_ut.c @@ -451,6 +451,7 @@ test_nvme_tcp_req_complete_safe(void) tcp_req.tqpair = &tqpair; tcp_req.state = NVME_TCP_REQ_ACTIVE; TAILQ_INIT(&tcp_req.tqpair->outstanding_reqs); + tqpair.qpair.num_outstanding_reqs = 1; /* Test case 1: send operation and transfer completed. Expect: PASS */ tcp_req.state = NVME_TCP_REQ_ACTIVE; @@ -460,14 +461,17 @@ test_nvme_tcp_req_complete_safe(void) rc = nvme_tcp_req_complete_safe(&tcp_req); CU_ASSERT(rc == true); + CU_ASSERT(tqpair.qpair.num_outstanding_reqs == 0); /* Test case 2: send operation not completed. Expect: FAIL */ tcp_req.ordering.raw = 0; tcp_req.state = NVME_TCP_REQ_ACTIVE; TAILQ_INSERT_TAIL(&tcp_req.tqpair->outstanding_reqs, &tcp_req, link); + tqpair.qpair.num_outstanding_reqs = 1; rc = nvme_tcp_req_complete_safe(&tcp_req); SPDK_CU_ASSERT_FATAL(rc != true); + CU_ASSERT(tqpair.qpair.num_outstanding_reqs == 1); TAILQ_REMOVE(&tcp_req.tqpair->outstanding_reqs, &tcp_req, link); /* Test case 3: in completion context. Expect: PASS */ @@ -477,10 +481,12 @@ test_nvme_tcp_req_complete_safe(void) tcp_req.ordering.bits.data_recv = 1; tcp_req.state = NVME_TCP_REQ_ACTIVE; TAILQ_INSERT_TAIL(&tcp_req.tqpair->outstanding_reqs, &tcp_req, link); + tqpair.qpair.num_outstanding_reqs = 1; rc = nvme_tcp_req_complete_safe(&tcp_req); CU_ASSERT(rc == true); CU_ASSERT(tcp_req.tqpair->async_complete == 0); + CU_ASSERT(tqpair.qpair.num_outstanding_reqs == 0); /* Test case 4: in async complete. Expect: PASS */ tqpair.qpair.in_completion_context = 0; @@ -488,10 +494,12 @@ test_nvme_tcp_req_complete_safe(void) tcp_req.ordering.bits.data_recv = 1; tcp_req.state = NVME_TCP_REQ_ACTIVE; TAILQ_INSERT_TAIL(&tcp_req.tqpair->outstanding_reqs, &tcp_req, link); + tqpair.qpair.num_outstanding_reqs = 1; rc = nvme_tcp_req_complete_safe(&tcp_req); CU_ASSERT(rc == true); CU_ASSERT(tcp_req.tqpair->async_complete); + CU_ASSERT(tqpair.qpair.num_outstanding_reqs == 0); } static void @@ -1138,6 +1146,7 @@ test_nvme_tcp_c2h_payload_handle(void) tcp_req.ordering.bits.data_recv = 0; tqpair.recv_state = NVME_TCP_PDU_RECV_STATE_ERROR; TAILQ_INSERT_TAIL(&tcp_req.tqpair->outstanding_reqs, &tcp_req, link); + tqpair.qpair.num_outstanding_reqs = 1; nvme_tcp_c2h_data_payload_handle(&tqpair, &pdu, &reaped); @@ -1146,6 +1155,7 @@ test_nvme_tcp_c2h_payload_handle(void) CU_ASSERT(tcp_req.rsp.sqid == tqpair.qpair.id); CU_ASSERT(tcp_req.ordering.bits.data_recv == 1); CU_ASSERT(reaped == 2); + CU_ASSERT(tqpair.qpair.num_outstanding_reqs == 0); /* case 2: nvme_tcp_c2h_data_payload_handle: tcp_req->datao == tcp_req->req->payload_size */ tcp_req.datao = 1024; @@ -1156,6 +1166,7 @@ test_nvme_tcp_c2h_payload_handle(void) tcp_req.ordering.bits.data_recv = 0; tqpair.recv_state = NVME_TCP_PDU_RECV_STATE_ERROR; TAILQ_INSERT_TAIL(&tcp_req.tqpair->outstanding_reqs, &tcp_req, link); + tqpair.qpair.num_outstanding_reqs = 1; nvme_tcp_c2h_data_payload_handle(&tqpair, &pdu, &reaped); @@ -1164,6 +1175,7 @@ test_nvme_tcp_c2h_payload_handle(void) CU_ASSERT(tcp_req.rsp.sqid == tqpair.qpair.id); CU_ASSERT(tcp_req.ordering.bits.data_recv == 1); CU_ASSERT(reaped == 3); + CU_ASSERT(tqpair.qpair.num_outstanding_reqs == 0); /* case 3: nvme_tcp_c2h_data_payload_handle: flag does not have SPDK_NVME_TCP_C2H_DATA_FLAGS_SUCCESS */ pdu.hdr.c2h_data.common.flags = SPDK_NVME_TCP_C2H_DATA_FLAGS_LAST_PDU; @@ -1175,16 +1187,19 @@ test_nvme_tcp_c2h_payload_handle(void) tcp_req.ordering.bits.data_recv = 0; tqpair.recv_state = NVME_TCP_PDU_RECV_STATE_ERROR; TAILQ_INSERT_TAIL(&tcp_req.tqpair->outstanding_reqs, &tcp_req, link); + tqpair.qpair.num_outstanding_reqs = 1; nvme_tcp_c2h_data_payload_handle(&tqpair, &pdu, &reaped); CU_ASSERT(reaped == 3); + CU_ASSERT(tqpair.qpair.num_outstanding_reqs == 1); /* case 4: nvme_tcp_c2h_term_req_payload_handle: recv_state is NVME_TCP_PDU_RECV_STATE_ERROR */ pdu.hdr.term_req.fes = SPDK_NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; nvme_tcp_c2h_term_req_payload_handle(&tqpair, &pdu); CU_ASSERT(tqpair.recv_state == NVME_TCP_PDU_RECV_STATE_ERROR); + CU_ASSERT(tqpair.qpair.num_outstanding_reqs == 1); } static void @@ -1281,6 +1296,7 @@ test_nvme_tcp_pdu_payload_handle(void) tcp_req.cid = 1; TAILQ_INIT(&tcp_req.tqpair->outstanding_reqs); TAILQ_INSERT_TAIL(&tcp_req.tqpair->outstanding_reqs, &tcp_req, link); + tqpair.qpair.num_outstanding_reqs = 1; /* C2H_DATA */ recv_pdu.hdr.common.pdu_type = SPDK_NVME_TCP_PDU_TYPE_C2H_DATA; @@ -1297,6 +1313,7 @@ test_nvme_tcp_pdu_payload_handle(void) CU_ASSERT(tcp_req.rsp.sqid == 1); CU_ASSERT(tcp_req.ordering.bits.data_recv == 1); CU_ASSERT(reaped == 1); + CU_ASSERT(tqpair.qpair.num_outstanding_reqs == 0); /* TermResp */ recv_pdu.hdr.common.pdu_type = SPDK_NVME_TCP_PDU_TYPE_C2H_TERM_REQ; @@ -1338,6 +1355,7 @@ test_nvme_tcp_capsule_resp_hdr_handle(void) memset(&rccqe_tgt, 0xff, sizeof(rccqe_tgt)); rccqe_tgt.cid = 0; memcpy(&tqpair.recv_pdu->hdr.capsule_resp.rccqe, &rccqe_tgt, sizeof(rccqe_tgt)); + tqpair.qpair.num_outstanding_reqs = 1; nvme_tcp_capsule_resp_hdr_handle(&tqpair, tqpair.recv_pdu, &reaped); CU_ASSERT(tqpair.recv_state == NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_READY); @@ -1345,6 +1363,7 @@ test_nvme_tcp_capsule_resp_hdr_handle(void) CU_ASSERT(tcp_req->ordering.bits.data_recv == 1); CU_ASSERT(reaped == 1); CU_ASSERT(TAILQ_EMPTY(&tcp_req->tqpair->outstanding_reqs)); + CU_ASSERT(tqpair.qpair.num_outstanding_reqs == 0); /* Get tcp request error, expect fail */ reaped = 0; @@ -1527,6 +1546,7 @@ test_nvme_tcp_ctrlr_delete_io_qpair(void) tcp_req.state = NVME_TCP_REQ_ACTIVE; TAILQ_INIT(&tqpair->outstanding_reqs); TAILQ_INSERT_TAIL(&tcp_req.tqpair->outstanding_reqs, &tcp_req, link); + qpair->num_outstanding_reqs = 1; rc = nvme_tcp_ctrlr_delete_io_qpair(&ctrlr, qpair);