2018-08-02 02:21:45 +00:00
|
|
|
/*-
|
|
|
|
* BSD LICENSE
|
|
|
|
*
|
2020-01-29 14:50:14 +00:00
|
|
|
* Copyright (c) Intel Corporation. All rights reserved.
|
|
|
|
* Copyright (c) 2020 Mellanox Technologies LTD. All rights reserved.
|
2021-12-23 17:18:50 +00:00
|
|
|
* Copyright (c) 2021, 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
2018-08-02 02:21:45 +00:00
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
*
|
|
|
|
* * Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in
|
|
|
|
* the documentation and/or other materials provided with the
|
|
|
|
* distribution.
|
|
|
|
* * Neither the name of Intel Corporation nor the names of its
|
|
|
|
* contributors may be used to endorse or promote products derived
|
|
|
|
* from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NVMe/TCP transport
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "nvme_internal.h"
|
|
|
|
|
|
|
|
#include "spdk/endian.h"
|
|
|
|
#include "spdk/likely.h"
|
|
|
|
#include "spdk/string.h"
|
|
|
|
#include "spdk/stdinc.h"
|
|
|
|
#include "spdk/crc32.h"
|
|
|
|
#include "spdk/endian.h"
|
|
|
|
#include "spdk/assert.h"
|
2019-01-24 23:15:42 +00:00
|
|
|
#include "spdk/string.h"
|
2018-08-02 02:21:45 +00:00
|
|
|
#include "spdk/thread.h"
|
|
|
|
#include "spdk/trace.h"
|
|
|
|
#include "spdk/util.h"
|
|
|
|
|
|
|
|
#include "spdk_internal/nvme_tcp.h"
|
|
|
|
|
|
|
|
#define NVME_TCP_RW_BUFFER_SIZE 131072
|
2019-12-07 14:29:11 +00:00
|
|
|
#define NVME_TCP_TIME_OUT_IN_SECONDS 2
|
2018-08-02 02:21:45 +00:00
|
|
|
|
|
|
|
#define NVME_TCP_HPDA_DEFAULT 0
|
2019-05-27 10:06:28 +00:00
|
|
|
#define NVME_TCP_MAX_R2T_DEFAULT 1
|
2018-08-02 02:21:45 +00:00
|
|
|
#define NVME_TCP_PDU_H2C_MIN_DATA_SIZE 4096
|
|
|
|
|
|
|
|
/* NVMe TCP transport extensions for spdk_nvme_ctrlr */
|
|
|
|
struct nvme_tcp_ctrlr {
|
|
|
|
struct spdk_nvme_ctrlr ctrlr;
|
|
|
|
};
|
|
|
|
|
2020-02-06 19:43:31 +00:00
|
|
|
struct nvme_tcp_poll_group {
|
|
|
|
struct spdk_nvme_transport_poll_group group;
|
2020-02-13 21:52:59 +00:00
|
|
|
struct spdk_sock_group *sock_group;
|
|
|
|
uint32_t completions_per_qpair;
|
|
|
|
int64_t num_completions;
|
2021-03-05 21:00:12 +00:00
|
|
|
|
|
|
|
TAILQ_HEAD(, nvme_tcp_qpair) needs_poll;
|
2021-07-19 08:57:48 +00:00
|
|
|
struct spdk_nvme_tcp_stat stats;
|
2020-02-06 19:43:31 +00:00
|
|
|
};
|
|
|
|
|
2018-08-02 02:21:45 +00:00
|
|
|
/* NVMe TCP qpair extensions for spdk_nvme_qpair */
|
|
|
|
struct nvme_tcp_qpair {
|
|
|
|
struct spdk_nvme_qpair qpair;
|
|
|
|
struct spdk_sock *sock;
|
|
|
|
|
|
|
|
TAILQ_HEAD(, nvme_tcp_req) free_reqs;
|
|
|
|
TAILQ_HEAD(, nvme_tcp_req) outstanding_reqs;
|
|
|
|
|
|
|
|
TAILQ_HEAD(, nvme_tcp_pdu) send_queue;
|
2021-05-06 14:13:11 +00:00
|
|
|
struct nvme_tcp_pdu *recv_pdu;
|
2020-07-30 11:43:51 +00:00
|
|
|
struct nvme_tcp_pdu *send_pdu; /* only for error pdu and init pdu */
|
2020-06-19 11:54:13 +00:00
|
|
|
struct nvme_tcp_pdu *send_pdus; /* Used by tcp_reqs */
|
2018-08-02 02:21:45 +00:00
|
|
|
enum nvme_tcp_pdu_recv_state recv_state;
|
|
|
|
struct nvme_tcp_req *tcp_reqs;
|
2021-07-19 08:57:48 +00:00
|
|
|
struct spdk_nvme_tcp_stat *stats;
|
2018-08-02 02:21:45 +00:00
|
|
|
|
|
|
|
uint16_t num_entries;
|
2020-09-01 08:53:46 +00:00
|
|
|
uint16_t async_complete;
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2020-09-01 08:23:25 +00:00
|
|
|
struct {
|
|
|
|
uint16_t host_hdgst_enable: 1;
|
|
|
|
uint16_t host_ddgst_enable: 1;
|
2020-09-01 08:33:27 +00:00
|
|
|
uint16_t icreq_send_ack: 1;
|
2021-07-02 08:17:52 +00:00
|
|
|
uint16_t in_connect_poll: 1;
|
|
|
|
uint16_t reserved: 12;
|
2020-09-01 08:23:25 +00:00
|
|
|
} flags;
|
2018-08-02 02:21:45 +00:00
|
|
|
|
|
|
|
/** Specifies the maximum number of PDU-Data bytes per H2C Data Transfer PDU */
|
|
|
|
uint32_t maxh2cdata;
|
|
|
|
|
2019-05-27 10:06:28 +00:00
|
|
|
uint32_t maxr2t;
|
2018-08-02 02:21:45 +00:00
|
|
|
|
|
|
|
/* 0 based value, which is used to guide the padding */
|
|
|
|
uint8_t cpda;
|
|
|
|
|
|
|
|
enum nvme_tcp_qpair_state state;
|
2021-03-05 21:00:12 +00:00
|
|
|
|
|
|
|
TAILQ_ENTRY(nvme_tcp_qpair) link;
|
|
|
|
bool needs_poll;
|
2021-05-28 16:47:16 +00:00
|
|
|
|
|
|
|
uint64_t icreq_timeout_tsc;
|
2021-12-21 01:33:37 +00:00
|
|
|
|
|
|
|
bool shared_stats;
|
2018-08-02 02:21:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
enum nvme_tcp_req_state {
|
|
|
|
NVME_TCP_REQ_FREE,
|
|
|
|
NVME_TCP_REQ_ACTIVE,
|
|
|
|
NVME_TCP_REQ_ACTIVE_R2T,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct nvme_tcp_req {
|
|
|
|
struct nvme_request *req;
|
|
|
|
enum nvme_tcp_req_state state;
|
|
|
|
uint16_t cid;
|
|
|
|
uint16_t ttag;
|
|
|
|
uint32_t datao;
|
nvme/tcp: Fix tcp_req->datao calculation issue.
When data digest is enabled for a nvme tcp qpair, we can use accel_fw
to calculate the data crc32c. Then if there are multiple
c2h pdus are coming, we can use both CPU resource directly
and accel_fw framework to caculate the checksum. Then the datao value compare
will not match since we will not update "datao" in the pdu coming order.
For example, if we receive 4 pdus, named as A, B, C, D.
offset data_len (in bytes)
A: 0 8192
B: 8192 4096
C: 12288 8192
D: 20480 4096
For receving the pdu, we hope that we can continue exeution even if
we use the offloading engine in accel_fw. Then in this situation,
if Pdu(C) is offloaded by accel_fw. Then our logic will continue receving
PDU(D). And according to the logic in our code, this time we leverage CPU
to calculate crc32c (Because we only have one active pdu to receive data).
Then we find the expected data offset is still 12288. Because "datao" in tcp_req will
only be updated after calling nvme_tcp_c2h_data_payload_handle function. So
while we enter nvme_tcp_c2h_data_hdr_handle function, we will find the
expected datao value is not as expected compared with the data offset value
contained in Pdu(D).
So the solution is that we create a new variable "expected_datao"
in tcp_req to do the comparation because we want to comply with the tp8000 spec
and do the offset check.
We still need use "datao" to count whether we receive the whole data or not.
So we cannot reuse "datao" variable in an early way. Otherwise, we will
release tcp_req structure early and cause another bug.
PS: This bug was not found early because previously the sw path in accel_fw
directly calculated the crc32c and called the user callback. Now we use a list and the
poller to handle, then it triggers this issue. Definitely, it will be much easier to
trigger this issue if we use real hardware engine.
Fixes #2098
Signed-off-by: Ziye Yang <ziye.yang@intel.com>
Change-Id: I10f5938a6342028d08d90820b2c14e4260134d77
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/9612
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Changpeng Liu <changpeng.liu@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: GangCao <gang.cao@intel.com>
Community-CI: Broadcom CI <spdk-ci.pdl@broadcom.com>
Community-CI: Mellanox Build Bot
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
2021-09-24 15:34:23 +00:00
|
|
|
uint32_t expected_datao;
|
2018-08-02 02:21:45 +00:00
|
|
|
uint32_t r2tl_remain;
|
2019-05-27 10:06:28 +00:00
|
|
|
uint32_t active_r2ts;
|
2020-09-01 07:43:51 +00:00
|
|
|
/* Used to hold a value received from subsequent R2T while we are still
|
|
|
|
* waiting for H2C complete */
|
|
|
|
uint16_t ttag_r2t_next;
|
2018-08-02 02:21:45 +00:00
|
|
|
bool in_capsule_data;
|
2021-05-04 23:06:47 +00:00
|
|
|
bool pdu_in_use;
|
nvme/tcp: Fix nvme_tcp_req free conflict between cmd sending and incoming pdu receiving
This patch tries to solve the out of order
call back handling for cmd sending and the incoming pdu handling.
Normally, the cmd call back will be called before
receving the next PDU from the target if the application
uses the sync manner.
With the uring implementation, after sending the
cmd to the target, we may have the following scenerio:
(1) Firstly receive the incoming pdu(e.g., CapsuleResp pdu, C2hdata pdu)
due to the group polling read event.
(2) Secondly execute the callback function related with NVMe command sending.
This means that the data from the initiator is really sent out to the target,
and the target receives, then sending back the data to the initiator. But the
uring io_uring_cqe event is not handled, thus if we execute
(1) first, it will clean the data structures related with nvme_tcp_req, and the
nvme_tcp_req will be used for other purpose. Then causes wrong behaviour like
the following:
"Rand Write test failed at QD=128 because fio hangs with the following error:
nvme_tcp.c: 971:nvme_tcp_capsule_resp_hdr_handle: *ERROR*:
no tcp_req is found with cid=66 for tqpair=0x7f23d8001710".
And this patch can address this issue.
Signed-off-by: Ziye Yang <ziye.yang@intel.com>
Change-Id: I5043aaa8adf5033d93dedac15f633f0850e0b9f2
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/2818
Community-CI: Mellanox Build Bot
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: Aleksey Marchuk <alexeymar@mellanox.com>
2020-06-09 15:36:04 +00:00
|
|
|
/* It is used to track whether the req can be safely freed */
|
2020-09-01 07:03:16 +00:00
|
|
|
union {
|
|
|
|
uint8_t raw;
|
|
|
|
struct {
|
|
|
|
/* The last send operation completed - kernel released send buffer */
|
|
|
|
uint8_t send_ack : 1;
|
|
|
|
/* Data transfer completed - target send resp or last data bit */
|
|
|
|
uint8_t data_recv : 1;
|
2020-09-01 07:18:33 +00:00
|
|
|
/* tcp_req is waiting for completion of the previous send operation (buffer reclaim notification
|
|
|
|
* from kernel) to send H2C */
|
|
|
|
uint8_t h2c_send_waiting_ack : 1;
|
2020-09-01 07:43:51 +00:00
|
|
|
/* tcp_req received subsequent r2t while it is still waiting for send_ack.
|
|
|
|
* Rare case, actual when dealing with target that can send several R2T requests.
|
|
|
|
* SPDK TCP target sends 1 R2T for the whole data buffer */
|
|
|
|
uint8_t r2t_waiting_h2c_complete : 1;
|
|
|
|
uint8_t reserved : 4;
|
2020-09-01 07:03:16 +00:00
|
|
|
} bits;
|
nvme/tcp: Fix nvme_tcp_req free conflict between cmd sending and incoming pdu receiving
This patch tries to solve the out of order
call back handling for cmd sending and the incoming pdu handling.
Normally, the cmd call back will be called before
receving the next PDU from the target if the application
uses the sync manner.
With the uring implementation, after sending the
cmd to the target, we may have the following scenerio:
(1) Firstly receive the incoming pdu(e.g., CapsuleResp pdu, C2hdata pdu)
due to the group polling read event.
(2) Secondly execute the callback function related with NVMe command sending.
This means that the data from the initiator is really sent out to the target,
and the target receives, then sending back the data to the initiator. But the
uring io_uring_cqe event is not handled, thus if we execute
(1) first, it will clean the data structures related with nvme_tcp_req, and the
nvme_tcp_req will be used for other purpose. Then causes wrong behaviour like
the following:
"Rand Write test failed at QD=128 because fio hangs with the following error:
nvme_tcp.c: 971:nvme_tcp_capsule_resp_hdr_handle: *ERROR*:
no tcp_req is found with cid=66 for tqpair=0x7f23d8001710".
And this patch can address this issue.
Signed-off-by: Ziye Yang <ziye.yang@intel.com>
Change-Id: I5043aaa8adf5033d93dedac15f633f0850e0b9f2
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/2818
Community-CI: Mellanox Build Bot
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: Aleksey Marchuk <alexeymar@mellanox.com>
2020-06-09 15:36:04 +00:00
|
|
|
} ordering;
|
2021-05-25 15:12:20 +00:00
|
|
|
struct nvme_tcp_pdu *pdu;
|
2019-03-18 10:07:04 +00:00
|
|
|
struct iovec iov[NVME_TCP_MAX_SGL_DESCRIPTORS];
|
|
|
|
uint32_t iovcnt;
|
2020-09-01 07:43:51 +00:00
|
|
|
/* Used to hold a value received from subsequent R2T while we are still
|
|
|
|
* waiting for H2C ack */
|
|
|
|
uint32_t r2tl_remain_next;
|
nvme/tcp: Fix nvme_tcp_req free conflict between cmd sending and incoming pdu receiving
This patch tries to solve the out of order
call back handling for cmd sending and the incoming pdu handling.
Normally, the cmd call back will be called before
receving the next PDU from the target if the application
uses the sync manner.
With the uring implementation, after sending the
cmd to the target, we may have the following scenerio:
(1) Firstly receive the incoming pdu(e.g., CapsuleResp pdu, C2hdata pdu)
due to the group polling read event.
(2) Secondly execute the callback function related with NVMe command sending.
This means that the data from the initiator is really sent out to the target,
and the target receives, then sending back the data to the initiator. But the
uring io_uring_cqe event is not handled, thus if we execute
(1) first, it will clean the data structures related with nvme_tcp_req, and the
nvme_tcp_req will be used for other purpose. Then causes wrong behaviour like
the following:
"Rand Write test failed at QD=128 because fio hangs with the following error:
nvme_tcp.c: 971:nvme_tcp_capsule_resp_hdr_handle: *ERROR*:
no tcp_req is found with cid=66 for tqpair=0x7f23d8001710".
And this patch can address this issue.
Signed-off-by: Ziye Yang <ziye.yang@intel.com>
Change-Id: I5043aaa8adf5033d93dedac15f633f0850e0b9f2
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/2818
Community-CI: Mellanox Build Bot
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: Aleksey Marchuk <alexeymar@mellanox.com>
2020-06-09 15:36:04 +00:00
|
|
|
struct nvme_tcp_qpair *tqpair;
|
2018-08-02 02:21:45 +00:00
|
|
|
TAILQ_ENTRY(nvme_tcp_req) link;
|
2020-09-01 08:15:09 +00:00
|
|
|
struct spdk_nvme_cpl rsp;
|
2018-08-02 02:21:45 +00:00
|
|
|
};
|
|
|
|
|
2021-12-21 01:33:37 +00:00
|
|
|
static struct spdk_nvme_tcp_stat g_dummy_stats = {};
|
|
|
|
|
2020-05-10 23:32:35 +00:00
|
|
|
static void nvme_tcp_send_h2c_data(struct nvme_tcp_req *tcp_req);
|
2020-09-01 08:37:36 +00:00
|
|
|
static int64_t nvme_tcp_poll_group_process_completions(struct spdk_nvme_transport_poll_group
|
|
|
|
*tgroup, uint32_t completions_per_qpair, spdk_nvme_disconnected_qpair_cb disconnected_qpair_cb);
|
2020-09-01 08:33:27 +00:00
|
|
|
static void nvme_tcp_icresp_handle(struct nvme_tcp_qpair *tqpair, struct nvme_tcp_pdu *pdu);
|
2018-08-02 02:21:45 +00:00
|
|
|
|
|
|
|
static inline struct nvme_tcp_qpair *
|
|
|
|
nvme_tcp_qpair(struct spdk_nvme_qpair *qpair)
|
|
|
|
{
|
|
|
|
assert(qpair->trtype == SPDK_NVME_TRANSPORT_TCP);
|
|
|
|
return SPDK_CONTAINEROF(qpair, struct nvme_tcp_qpair, qpair);
|
|
|
|
}
|
|
|
|
|
2020-02-13 21:52:59 +00:00
|
|
|
static inline struct nvme_tcp_poll_group *
|
|
|
|
nvme_tcp_poll_group(struct spdk_nvme_transport_poll_group *group)
|
|
|
|
{
|
|
|
|
return SPDK_CONTAINEROF(group, struct nvme_tcp_poll_group, group);
|
|
|
|
}
|
|
|
|
|
2018-08-02 02:21:45 +00:00
|
|
|
static inline struct nvme_tcp_ctrlr *
|
|
|
|
nvme_tcp_ctrlr(struct spdk_nvme_ctrlr *ctrlr)
|
|
|
|
{
|
|
|
|
assert(ctrlr->trid.trtype == SPDK_NVME_TRANSPORT_TCP);
|
|
|
|
return SPDK_CONTAINEROF(ctrlr, struct nvme_tcp_ctrlr, ctrlr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct nvme_tcp_req *
|
|
|
|
nvme_tcp_req_get(struct nvme_tcp_qpair *tqpair)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_req *tcp_req;
|
|
|
|
|
|
|
|
tcp_req = TAILQ_FIRST(&tqpair->free_reqs);
|
|
|
|
if (!tcp_req) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(tcp_req->state == NVME_TCP_REQ_FREE);
|
|
|
|
tcp_req->state = NVME_TCP_REQ_ACTIVE;
|
|
|
|
TAILQ_REMOVE(&tqpair->free_reqs, tcp_req, link);
|
|
|
|
tcp_req->datao = 0;
|
nvme/tcp: Fix tcp_req->datao calculation issue.
When data digest is enabled for a nvme tcp qpair, we can use accel_fw
to calculate the data crc32c. Then if there are multiple
c2h pdus are coming, we can use both CPU resource directly
and accel_fw framework to caculate the checksum. Then the datao value compare
will not match since we will not update "datao" in the pdu coming order.
For example, if we receive 4 pdus, named as A, B, C, D.
offset data_len (in bytes)
A: 0 8192
B: 8192 4096
C: 12288 8192
D: 20480 4096
For receving the pdu, we hope that we can continue exeution even if
we use the offloading engine in accel_fw. Then in this situation,
if Pdu(C) is offloaded by accel_fw. Then our logic will continue receving
PDU(D). And according to the logic in our code, this time we leverage CPU
to calculate crc32c (Because we only have one active pdu to receive data).
Then we find the expected data offset is still 12288. Because "datao" in tcp_req will
only be updated after calling nvme_tcp_c2h_data_payload_handle function. So
while we enter nvme_tcp_c2h_data_hdr_handle function, we will find the
expected datao value is not as expected compared with the data offset value
contained in Pdu(D).
So the solution is that we create a new variable "expected_datao"
in tcp_req to do the comparation because we want to comply with the tp8000 spec
and do the offset check.
We still need use "datao" to count whether we receive the whole data or not.
So we cannot reuse "datao" variable in an early way. Otherwise, we will
release tcp_req structure early and cause another bug.
PS: This bug was not found early because previously the sw path in accel_fw
directly calculated the crc32c and called the user callback. Now we use a list and the
poller to handle, then it triggers this issue. Definitely, it will be much easier to
trigger this issue if we use real hardware engine.
Fixes #2098
Signed-off-by: Ziye Yang <ziye.yang@intel.com>
Change-Id: I10f5938a6342028d08d90820b2c14e4260134d77
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/9612
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Changpeng Liu <changpeng.liu@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: GangCao <gang.cao@intel.com>
Community-CI: Broadcom CI <spdk-ci.pdl@broadcom.com>
Community-CI: Mellanox Build Bot
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
2021-09-24 15:34:23 +00:00
|
|
|
tcp_req->expected_datao = 0;
|
2018-08-02 02:21:45 +00:00
|
|
|
tcp_req->req = NULL;
|
|
|
|
tcp_req->in_capsule_data = false;
|
2021-05-04 23:06:47 +00:00
|
|
|
tcp_req->pdu_in_use = false;
|
2018-08-02 02:21:45 +00:00
|
|
|
tcp_req->r2tl_remain = 0;
|
2020-09-01 07:43:51 +00:00
|
|
|
tcp_req->r2tl_remain_next = 0;
|
2019-05-27 10:06:28 +00:00
|
|
|
tcp_req->active_r2ts = 0;
|
2019-03-18 10:07:04 +00:00
|
|
|
tcp_req->iovcnt = 0;
|
2020-09-01 07:03:16 +00:00
|
|
|
tcp_req->ordering.raw = 0;
|
2021-05-25 15:12:20 +00:00
|
|
|
memset(tcp_req->pdu, 0, sizeof(struct nvme_tcp_pdu));
|
2020-09-01 08:15:09 +00:00
|
|
|
memset(&tcp_req->rsp, 0, sizeof(struct spdk_nvme_cpl));
|
2018-08-02 02:21:45 +00:00
|
|
|
TAILQ_INSERT_TAIL(&tqpair->outstanding_reqs, tcp_req, link);
|
|
|
|
|
|
|
|
return tcp_req;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nvme_tcp_req_put(struct nvme_tcp_qpair *tqpair, struct nvme_tcp_req *tcp_req)
|
|
|
|
{
|
|
|
|
assert(tcp_req->state != NVME_TCP_REQ_FREE);
|
|
|
|
tcp_req->state = NVME_TCP_REQ_FREE;
|
2020-07-10 06:07:18 +00:00
|
|
|
TAILQ_INSERT_HEAD(&tqpair->free_reqs, tcp_req, link);
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
nvme_tcp_parse_addr(struct sockaddr_storage *sa, int family, const char *addr, const char *service)
|
|
|
|
{
|
|
|
|
struct addrinfo *res;
|
|
|
|
struct addrinfo hints;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
|
|
hints.ai_family = family;
|
|
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
hints.ai_protocol = 0;
|
|
|
|
|
|
|
|
ret = getaddrinfo(addr, service, &hints, &res);
|
|
|
|
if (ret) {
|
|
|
|
SPDK_ERRLOG("getaddrinfo failed: %s (%d)\n", gai_strerror(ret), ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (res->ai_addrlen > sizeof(*sa)) {
|
|
|
|
SPDK_ERRLOG("getaddrinfo() ai_addrlen %zu too large\n", (size_t)res->ai_addrlen);
|
2020-08-17 12:46:05 +00:00
|
|
|
ret = -EINVAL;
|
2018-08-02 02:21:45 +00:00
|
|
|
} else {
|
|
|
|
memcpy(sa, res->ai_addr, res->ai_addrlen);
|
|
|
|
}
|
|
|
|
|
|
|
|
freeaddrinfo(res);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nvme_tcp_free_reqs(struct nvme_tcp_qpair *tqpair)
|
|
|
|
{
|
|
|
|
free(tqpair->tcp_reqs);
|
|
|
|
tqpair->tcp_reqs = NULL;
|
2020-06-19 11:54:13 +00:00
|
|
|
|
|
|
|
spdk_free(tqpair->send_pdus);
|
|
|
|
tqpair->send_pdus = NULL;
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
nvme_tcp_alloc_reqs(struct nvme_tcp_qpair *tqpair)
|
|
|
|
{
|
2020-01-31 14:54:49 +00:00
|
|
|
uint16_t i;
|
2018-08-02 02:21:45 +00:00
|
|
|
struct nvme_tcp_req *tcp_req;
|
|
|
|
|
|
|
|
tqpair->tcp_reqs = calloc(tqpair->num_entries, sizeof(struct nvme_tcp_req));
|
|
|
|
if (tqpair->tcp_reqs == NULL) {
|
2020-06-19 11:54:13 +00:00
|
|
|
SPDK_ERRLOG("Failed to allocate tcp_reqs on tqpair=%p\n", tqpair);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2021-05-06 14:13:11 +00:00
|
|
|
/* Add additional 2 member for the send_pdu, recv_pdu owned by the tqpair */
|
|
|
|
tqpair->send_pdus = spdk_zmalloc((tqpair->num_entries + 2) * sizeof(struct nvme_tcp_pdu),
|
2020-06-19 11:54:13 +00:00
|
|
|
0x1000, NULL,
|
|
|
|
SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_DMA);
|
|
|
|
|
|
|
|
if (tqpair->send_pdus == NULL) {
|
|
|
|
SPDK_ERRLOG("Failed to allocate send_pdus on tqpair=%p\n", tqpair);
|
2018-08-02 02:21:45 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
TAILQ_INIT(&tqpair->send_queue);
|
|
|
|
TAILQ_INIT(&tqpair->free_reqs);
|
|
|
|
TAILQ_INIT(&tqpair->outstanding_reqs);
|
|
|
|
for (i = 0; i < tqpair->num_entries; i++) {
|
|
|
|
tcp_req = &tqpair->tcp_reqs[i];
|
|
|
|
tcp_req->cid = i;
|
nvme/tcp: Fix nvme_tcp_req free conflict between cmd sending and incoming pdu receiving
This patch tries to solve the out of order
call back handling for cmd sending and the incoming pdu handling.
Normally, the cmd call back will be called before
receving the next PDU from the target if the application
uses the sync manner.
With the uring implementation, after sending the
cmd to the target, we may have the following scenerio:
(1) Firstly receive the incoming pdu(e.g., CapsuleResp pdu, C2hdata pdu)
due to the group polling read event.
(2) Secondly execute the callback function related with NVMe command sending.
This means that the data from the initiator is really sent out to the target,
and the target receives, then sending back the data to the initiator. But the
uring io_uring_cqe event is not handled, thus if we execute
(1) first, it will clean the data structures related with nvme_tcp_req, and the
nvme_tcp_req will be used for other purpose. Then causes wrong behaviour like
the following:
"Rand Write test failed at QD=128 because fio hangs with the following error:
nvme_tcp.c: 971:nvme_tcp_capsule_resp_hdr_handle: *ERROR*:
no tcp_req is found with cid=66 for tqpair=0x7f23d8001710".
And this patch can address this issue.
Signed-off-by: Ziye Yang <ziye.yang@intel.com>
Change-Id: I5043aaa8adf5033d93dedac15f633f0850e0b9f2
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/2818
Community-CI: Mellanox Build Bot
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: Aleksey Marchuk <alexeymar@mellanox.com>
2020-06-09 15:36:04 +00:00
|
|
|
tcp_req->tqpair = tqpair;
|
2021-05-25 15:12:20 +00:00
|
|
|
tcp_req->pdu = &tqpair->send_pdus[i];
|
2018-08-02 02:21:45 +00:00
|
|
|
TAILQ_INSERT_TAIL(&tqpair->free_reqs, tcp_req, link);
|
|
|
|
}
|
|
|
|
|
2020-07-30 11:43:51 +00:00
|
|
|
tqpair->send_pdu = &tqpair->send_pdus[i];
|
2021-05-06 14:13:11 +00:00
|
|
|
tqpair->recv_pdu = &tqpair->send_pdus[i + 1];
|
2020-07-30 11:43:51 +00:00
|
|
|
|
2018-08-02 02:21:45 +00:00
|
|
|
return 0;
|
|
|
|
fail:
|
|
|
|
nvme_tcp_free_reqs(tqpair);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2020-02-10 19:13:06 +00:00
|
|
|
static void
|
2019-12-27 00:20:07 +00:00
|
|
|
nvme_tcp_ctrlr_disconnect_qpair(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair)
|
2019-05-09 19:43:50 +00:00
|
|
|
{
|
|
|
|
struct nvme_tcp_qpair *tqpair = nvme_tcp_qpair(qpair);
|
2019-06-04 12:56:45 +00:00
|
|
|
struct nvme_tcp_pdu *pdu;
|
2020-12-14 15:31:30 +00:00
|
|
|
int rc;
|
2021-03-05 21:00:12 +00:00
|
|
|
struct nvme_tcp_poll_group *group;
|
|
|
|
|
|
|
|
if (tqpair->needs_poll) {
|
|
|
|
group = nvme_tcp_poll_group(qpair->poll_group);
|
|
|
|
TAILQ_REMOVE(&group->needs_poll, tqpair, link);
|
|
|
|
tqpair->needs_poll = false;
|
|
|
|
}
|
2020-12-14 15:31:30 +00:00
|
|
|
|
|
|
|
rc = spdk_sock_close(&tqpair->sock);
|
2019-05-09 19:43:50 +00:00
|
|
|
|
2020-12-14 15:31:30 +00:00
|
|
|
if (tqpair->sock != NULL) {
|
|
|
|
SPDK_ERRLOG("tqpair=%p, errno=%d, rc=%d\n", tqpair, errno, rc);
|
|
|
|
/* Set it to NULL manually */
|
|
|
|
tqpair->sock = NULL;
|
|
|
|
}
|
2019-06-04 12:56:45 +00:00
|
|
|
|
|
|
|
/* clear the send_queue */
|
|
|
|
while (!TAILQ_EMPTY(&tqpair->send_queue)) {
|
|
|
|
pdu = TAILQ_FIRST(&tqpair->send_queue);
|
|
|
|
/* Remove the pdu from the send_queue to prevent the wrong sending out
|
|
|
|
* in the next round connection
|
|
|
|
*/
|
|
|
|
TAILQ_REMOVE(&tqpair->send_queue, pdu, tailq);
|
|
|
|
}
|
2019-05-09 19:43:50 +00:00
|
|
|
}
|
|
|
|
|
2020-02-10 19:13:06 +00:00
|
|
|
static void nvme_tcp_qpair_abort_reqs(struct spdk_nvme_qpair *qpair, uint32_t dnr);
|
|
|
|
|
|
|
|
static int
|
2019-12-27 00:20:07 +00:00
|
|
|
nvme_tcp_ctrlr_delete_io_qpair(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair)
|
2018-08-02 02:21:45 +00:00
|
|
|
{
|
|
|
|
struct nvme_tcp_qpair *tqpair;
|
|
|
|
|
2021-03-17 00:06:17 +00:00
|
|
|
assert(qpair != NULL);
|
2021-12-23 17:18:50 +00:00
|
|
|
nvme_tcp_qpair_abort_reqs(qpair, 0);
|
2018-08-02 02:21:45 +00:00
|
|
|
nvme_qpair_deinit(qpair);
|
|
|
|
tqpair = nvme_tcp_qpair(qpair);
|
|
|
|
nvme_tcp_free_reqs(tqpair);
|
2021-12-21 01:33:37 +00:00
|
|
|
if (!tqpair->shared_stats) {
|
|
|
|
free(tqpair->stats);
|
|
|
|
}
|
2018-08-02 02:21:45 +00:00
|
|
|
free(tqpair);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-02-10 19:13:06 +00:00
|
|
|
static int
|
2018-08-02 02:21:45 +00:00
|
|
|
nvme_tcp_ctrlr_enable(struct spdk_nvme_ctrlr *ctrlr)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-02-10 19:13:06 +00:00
|
|
|
static int
|
2018-08-02 02:21:45 +00:00
|
|
|
nvme_tcp_ctrlr_destruct(struct spdk_nvme_ctrlr *ctrlr)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_ctrlr *tctrlr = nvme_tcp_ctrlr(ctrlr);
|
|
|
|
|
|
|
|
if (ctrlr->adminq) {
|
2019-12-27 00:20:07 +00:00
|
|
|
nvme_tcp_ctrlr_delete_io_qpair(ctrlr, ctrlr->adminq);
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nvme_ctrlr_destruct_finish(ctrlr);
|
|
|
|
|
|
|
|
free(tctrlr);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-11-21 15:21:29 +00:00
|
|
|
static void
|
|
|
|
_pdu_write_done(void *cb_arg, int err)
|
2018-08-02 02:21:45 +00:00
|
|
|
{
|
2019-11-21 15:21:29 +00:00
|
|
|
struct nvme_tcp_pdu *pdu = cb_arg;
|
|
|
|
struct nvme_tcp_qpair *tqpair = pdu->qpair;
|
2021-09-19 22:06:37 +00:00
|
|
|
struct nvme_tcp_poll_group *pgroup;
|
2021-03-05 21:00:12 +00:00
|
|
|
|
|
|
|
/* If there are queued requests, we assume they are queued because they are waiting
|
|
|
|
* for resources to be released. Those resources are almost certainly released in
|
|
|
|
* response to a PDU completing here. However, to attempt to make forward progress
|
|
|
|
* the qpair needs to be polled and we can't rely on another network event to make
|
|
|
|
* that happen. Add it to a list of qpairs to poll regardless of network activity
|
|
|
|
* here. */
|
2021-09-19 22:06:37 +00:00
|
|
|
if (tqpair->qpair.poll_group && !STAILQ_EMPTY(&tqpair->qpair.queued_req) &&
|
|
|
|
!tqpair->needs_poll) {
|
|
|
|
pgroup = nvme_tcp_poll_group(tqpair->qpair.poll_group);
|
|
|
|
|
2021-03-05 21:00:12 +00:00
|
|
|
TAILQ_INSERT_TAIL(&pgroup->needs_poll, tqpair, link);
|
|
|
|
tqpair->needs_poll = true;
|
|
|
|
}
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2019-11-21 15:21:29 +00:00
|
|
|
TAILQ_REMOVE(&tqpair->send_queue, pdu, tailq);
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2019-11-21 15:21:29 +00:00
|
|
|
if (err != 0) {
|
2020-04-14 21:41:11 +00:00
|
|
|
nvme_transport_ctrlr_disconnect_qpair(tqpair->qpair.ctrlr, &tqpair->qpair);
|
2019-11-21 15:21:29 +00:00
|
|
|
return;
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
2019-11-21 15:21:29 +00:00
|
|
|
assert(pdu->cb_fn != NULL);
|
|
|
|
pdu->cb_fn(pdu->cb_arg);
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
2021-03-08 15:52:05 +00:00
|
|
|
static void
|
|
|
|
_tcp_write_pdu(struct nvme_tcp_pdu *pdu)
|
2018-08-02 02:21:45 +00:00
|
|
|
{
|
2019-11-21 15:21:29 +00:00
|
|
|
uint32_t mapped_length = 0;
|
2021-03-08 15:52:05 +00:00
|
|
|
struct nvme_tcp_qpair *tqpair = pdu->qpair;
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2021-03-08 15:52:05 +00:00
|
|
|
pdu->sock_req.iovcnt = nvme_tcp_build_iovs(pdu->iov, NVME_TCP_MAX_SGL_DESCRIPTORS, pdu,
|
|
|
|
(bool)tqpair->flags.host_hdgst_enable, (bool)tqpair->flags.host_ddgst_enable,
|
|
|
|
&mapped_length);
|
|
|
|
pdu->sock_req.cb_fn = _pdu_write_done;
|
|
|
|
pdu->sock_req.cb_arg = pdu;
|
|
|
|
TAILQ_INSERT_TAIL(&tqpair->send_queue, pdu, tailq);
|
2021-07-19 08:57:48 +00:00
|
|
|
tqpair->stats->submitted_requests++;
|
2021-03-08 15:52:05 +00:00
|
|
|
spdk_sock_writev_async(tqpair->sock, &pdu->sock_req);
|
|
|
|
}
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2021-03-08 15:52:05 +00:00
|
|
|
static void
|
|
|
|
data_crc32_accel_done(void *cb_arg, int status)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_pdu *pdu = cb_arg;
|
|
|
|
|
|
|
|
if (spdk_unlikely(status)) {
|
|
|
|
SPDK_ERRLOG("Failed to compute the data digest for pdu =%p\n", pdu);
|
|
|
|
_pdu_write_done(pdu, status);
|
|
|
|
return;
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
2021-03-08 15:52:05 +00:00
|
|
|
pdu->data_digest_crc32 ^= SPDK_CRC32C_XOR;
|
|
|
|
MAKE_DIGEST_WORD(pdu->data_digest, pdu->data_digest_crc32);
|
|
|
|
|
|
|
|
_tcp_write_pdu(pdu);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pdu_data_crc32_compute(struct nvme_tcp_pdu *pdu)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_qpair *tqpair = pdu->qpair;
|
|
|
|
uint32_t crc32c;
|
|
|
|
struct nvme_tcp_poll_group *tgroup = nvme_tcp_poll_group(tqpair->qpair.poll_group);
|
|
|
|
|
2018-08-02 02:21:45 +00:00
|
|
|
/* Data Digest */
|
2020-09-01 08:23:25 +00:00
|
|
|
if (pdu->data_len > 0 && g_nvme_tcp_ddgst[pdu->hdr.common.pdu_type] &&
|
|
|
|
tqpair->flags.host_ddgst_enable) {
|
2021-05-25 12:05:30 +00:00
|
|
|
/* Only suport this limited case for the first step */
|
|
|
|
if ((nvme_qpair_get_state(&tqpair->qpair) >= NVME_QPAIR_CONNECTED) &&
|
|
|
|
(tgroup != NULL && tgroup->group.group->accel_fn_table.submit_accel_crc32c) &&
|
2021-03-08 15:52:05 +00:00
|
|
|
spdk_likely(!pdu->dif_ctx && (pdu->data_len % SPDK_NVME_TCP_DIGEST_ALIGNMENT == 0))) {
|
|
|
|
tgroup->group.group->accel_fn_table.submit_accel_crc32c(tgroup->group.group->ctx,
|
|
|
|
&pdu->data_digest_crc32, pdu->data_iov,
|
|
|
|
pdu->data_iovcnt, 0, data_crc32_accel_done, pdu);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-08-02 02:21:45 +00:00
|
|
|
crc32c = nvme_tcp_pdu_calc_data_digest(pdu);
|
|
|
|
MAKE_DIGEST_WORD(pdu->data_digest, crc32c);
|
|
|
|
}
|
|
|
|
|
2021-03-08 15:52:05 +00:00
|
|
|
_tcp_write_pdu(pdu);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
nvme_tcp_qpair_write_pdu(struct nvme_tcp_qpair *tqpair,
|
|
|
|
struct nvme_tcp_pdu *pdu,
|
|
|
|
nvme_tcp_qpair_xfer_complete_cb cb_fn,
|
|
|
|
void *cb_arg)
|
|
|
|
{
|
|
|
|
int hlen;
|
|
|
|
uint32_t crc32c;
|
|
|
|
|
|
|
|
hlen = pdu->hdr.common.hlen;
|
2018-08-02 02:21:45 +00:00
|
|
|
pdu->cb_fn = cb_fn;
|
|
|
|
pdu->cb_arg = cb_arg;
|
2019-11-21 15:21:29 +00:00
|
|
|
pdu->qpair = tqpair;
|
2021-03-08 15:52:05 +00:00
|
|
|
|
|
|
|
/* Header Digest */
|
|
|
|
if (g_nvme_tcp_hdgst[pdu->hdr.common.pdu_type] && tqpair->flags.host_hdgst_enable) {
|
2021-05-24 16:46:33 +00:00
|
|
|
crc32c = nvme_tcp_pdu_calc_header_digest(pdu);
|
|
|
|
MAKE_DIGEST_WORD((uint8_t *)pdu->hdr.raw + hlen, crc32c);
|
2021-03-08 15:52:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pdu_data_crc32_compute(pdu);
|
2019-11-21 15:21:29 +00:00
|
|
|
|
2018-08-02 02:21:45 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Build SGL describing contiguous payload buffer.
|
|
|
|
*/
|
|
|
|
static int
|
2019-02-11 06:43:39 +00:00
|
|
|
nvme_tcp_build_contig_request(struct nvme_tcp_qpair *tqpair, struct nvme_tcp_req *tcp_req)
|
2018-08-02 02:21:45 +00:00
|
|
|
{
|
2019-02-11 06:43:39 +00:00
|
|
|
struct nvme_request *req = tcp_req->req;
|
2019-03-18 10:07:04 +00:00
|
|
|
|
|
|
|
tcp_req->iov[0].iov_base = req->payload.contig_or_cb_arg + req->payload_offset;
|
|
|
|
tcp_req->iov[0].iov_len = req->payload_size;
|
|
|
|
tcp_req->iovcnt = 1;
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "enter\n");
|
2018-08-02 02:21:45 +00:00
|
|
|
|
|
|
|
assert(nvme_payload_type(&req->payload) == NVME_PAYLOAD_TYPE_CONTIG);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Build SGL describing scattered payload buffer.
|
|
|
|
*/
|
|
|
|
static int
|
2019-02-11 06:43:39 +00:00
|
|
|
nvme_tcp_build_sgl_request(struct nvme_tcp_qpair *tqpair, struct nvme_tcp_req *tcp_req)
|
2018-08-02 02:21:45 +00:00
|
|
|
{
|
2020-01-31 14:54:49 +00:00
|
|
|
int rc;
|
2020-03-09 15:26:21 +00:00
|
|
|
uint32_t length, remaining_size, iovcnt = 0, max_num_sgl;
|
2019-02-11 06:43:39 +00:00
|
|
|
struct nvme_request *req = tcp_req->req;
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "enter\n");
|
2018-08-02 02:21:45 +00:00
|
|
|
|
|
|
|
assert(req->payload_size != 0);
|
|
|
|
assert(nvme_payload_type(&req->payload) == NVME_PAYLOAD_TYPE_SGL);
|
|
|
|
assert(req->payload.reset_sgl_fn != NULL);
|
|
|
|
assert(req->payload.next_sge_fn != NULL);
|
|
|
|
req->payload.reset_sgl_fn(req->payload.contig_or_cb_arg, req->payload_offset);
|
|
|
|
|
2020-03-09 15:26:21 +00:00
|
|
|
max_num_sgl = spdk_min(req->qpair->ctrlr->max_sges, NVME_TCP_MAX_SGL_DESCRIPTORS);
|
2019-03-18 10:07:04 +00:00
|
|
|
remaining_size = req->payload_size;
|
2019-02-11 06:43:39 +00:00
|
|
|
|
2019-03-18 10:07:04 +00:00
|
|
|
do {
|
|
|
|
rc = req->payload.next_sge_fn(req->payload.contig_or_cb_arg, &tcp_req->iov[iovcnt].iov_base,
|
|
|
|
&length);
|
|
|
|
if (rc) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-06-04 14:27:36 +00:00
|
|
|
length = spdk_min(length, remaining_size);
|
2019-03-18 10:07:04 +00:00
|
|
|
tcp_req->iov[iovcnt].iov_len = length;
|
|
|
|
remaining_size -= length;
|
|
|
|
iovcnt++;
|
2020-03-09 15:26:21 +00:00
|
|
|
} while (remaining_size > 0 && iovcnt < max_num_sgl);
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2019-03-18 10:07:04 +00:00
|
|
|
|
|
|
|
/* Should be impossible if we did our sgl checks properly up the stack, but do a sanity check here. */
|
|
|
|
if (remaining_size > 0) {
|
2020-03-09 15:26:21 +00:00
|
|
|
SPDK_ERRLOG("Failed to construct tcp_req=%p, and the iovcnt=%u, remaining_size=%u\n",
|
|
|
|
tcp_req, iovcnt, remaining_size);
|
2018-08-02 02:21:45 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-03-18 10:07:04 +00:00
|
|
|
tcp_req->iovcnt = iovcnt;
|
|
|
|
|
2018-08-02 02:21:45 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
nvme_tcp_req_init(struct nvme_tcp_qpair *tqpair, struct nvme_request *req,
|
|
|
|
struct nvme_tcp_req *tcp_req)
|
|
|
|
{
|
2018-12-03 02:18:36 +00:00
|
|
|
struct spdk_nvme_ctrlr *ctrlr = tqpair->qpair.ctrlr;
|
2018-08-02 02:21:45 +00:00
|
|
|
int rc = 0;
|
|
|
|
enum spdk_nvme_data_transfer xfer;
|
2021-11-25 01:40:58 +00:00
|
|
|
uint32_t max_in_capsule_data_size;
|
2018-08-02 02:21:45 +00:00
|
|
|
|
|
|
|
tcp_req->req = req;
|
|
|
|
req->cmd.cid = tcp_req->cid;
|
|
|
|
req->cmd.psdt = SPDK_NVME_PSDT_SGL_MPTR_CONTIG;
|
|
|
|
req->cmd.dptr.sgl1.unkeyed.type = SPDK_NVME_SGL_TYPE_TRANSPORT_DATA_BLOCK;
|
|
|
|
req->cmd.dptr.sgl1.unkeyed.subtype = SPDK_NVME_SGL_SUBTYPE_TRANSPORT;
|
|
|
|
req->cmd.dptr.sgl1.unkeyed.length = req->payload_size;
|
|
|
|
|
|
|
|
if (nvme_payload_type(&req->payload) == NVME_PAYLOAD_TYPE_CONTIG) {
|
2019-02-11 06:43:39 +00:00
|
|
|
rc = nvme_tcp_build_contig_request(tqpair, tcp_req);
|
2018-08-02 02:21:45 +00:00
|
|
|
} else if (nvme_payload_type(&req->payload) == NVME_PAYLOAD_TYPE_SGL) {
|
2019-02-11 06:43:39 +00:00
|
|
|
rc = nvme_tcp_build_sgl_request(tqpair, tcp_req);
|
2018-08-02 02:21:45 +00:00
|
|
|
} else {
|
|
|
|
rc = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rc) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (req->cmd.opc == SPDK_NVME_OPC_FABRIC) {
|
|
|
|
struct spdk_nvmf_capsule_cmd *nvmf_cmd = (struct spdk_nvmf_capsule_cmd *)&req->cmd;
|
|
|
|
|
|
|
|
xfer = spdk_nvme_opc_get_data_transfer(nvmf_cmd->fctype);
|
|
|
|
} else {
|
|
|
|
xfer = spdk_nvme_opc_get_data_transfer(req->cmd.opc);
|
|
|
|
}
|
2018-12-03 02:18:36 +00:00
|
|
|
if (xfer == SPDK_NVME_DATA_HOST_TO_CONTROLLER) {
|
2021-11-25 01:40:58 +00:00
|
|
|
max_in_capsule_data_size = ctrlr->ioccsz_bytes;
|
2018-12-03 02:18:36 +00:00
|
|
|
if ((req->cmd.opc == SPDK_NVME_OPC_FABRIC) || nvme_qpair_is_admin_queue(&tqpair->qpair)) {
|
2021-11-25 01:40:58 +00:00
|
|
|
max_in_capsule_data_size = SPDK_NVME_TCP_IN_CAPSULE_DATA_MAX_SIZE;
|
2018-12-03 02:18:36 +00:00
|
|
|
}
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2021-11-25 01:40:58 +00:00
|
|
|
if (req->payload_size <= max_in_capsule_data_size) {
|
2018-12-03 02:18:36 +00:00
|
|
|
req->cmd.dptr.sgl1.unkeyed.type = SPDK_NVME_SGL_TYPE_DATA_BLOCK;
|
|
|
|
req->cmd.dptr.sgl1.unkeyed.subtype = SPDK_NVME_SGL_SUBTYPE_OFFSET;
|
|
|
|
req->cmd.dptr.sgl1.address = 0;
|
|
|
|
tcp_req->in_capsule_data = true;
|
|
|
|
}
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-09-01 08:15:09 +00:00
|
|
|
static inline bool
|
|
|
|
nvme_tcp_req_complete_safe(struct nvme_tcp_req *tcp_req)
|
nvme/tcp: Fix nvme_tcp_req free conflict between cmd sending and incoming pdu receiving
This patch tries to solve the out of order
call back handling for cmd sending and the incoming pdu handling.
Normally, the cmd call back will be called before
receving the next PDU from the target if the application
uses the sync manner.
With the uring implementation, after sending the
cmd to the target, we may have the following scenerio:
(1) Firstly receive the incoming pdu(e.g., CapsuleResp pdu, C2hdata pdu)
due to the group polling read event.
(2) Secondly execute the callback function related with NVMe command sending.
This means that the data from the initiator is really sent out to the target,
and the target receives, then sending back the data to the initiator. But the
uring io_uring_cqe event is not handled, thus if we execute
(1) first, it will clean the data structures related with nvme_tcp_req, and the
nvme_tcp_req will be used for other purpose. Then causes wrong behaviour like
the following:
"Rand Write test failed at QD=128 because fio hangs with the following error:
nvme_tcp.c: 971:nvme_tcp_capsule_resp_hdr_handle: *ERROR*:
no tcp_req is found with cid=66 for tqpair=0x7f23d8001710".
And this patch can address this issue.
Signed-off-by: Ziye Yang <ziye.yang@intel.com>
Change-Id: I5043aaa8adf5033d93dedac15f633f0850e0b9f2
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/2818
Community-CI: Mellanox Build Bot
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: Aleksey Marchuk <alexeymar@mellanox.com>
2020-06-09 15:36:04 +00:00
|
|
|
{
|
2020-09-01 08:15:09 +00:00
|
|
|
struct spdk_nvme_cpl cpl;
|
|
|
|
spdk_nvme_cmd_cb user_cb;
|
|
|
|
void *user_cb_arg;
|
|
|
|
struct spdk_nvme_qpair *qpair;
|
|
|
|
struct nvme_request *req;
|
|
|
|
|
|
|
|
if (!(tcp_req->ordering.bits.send_ack && tcp_req->ordering.bits.data_recv)) {
|
|
|
|
return false;
|
nvme/tcp: Fix nvme_tcp_req free conflict between cmd sending and incoming pdu receiving
This patch tries to solve the out of order
call back handling for cmd sending and the incoming pdu handling.
Normally, the cmd call back will be called before
receving the next PDU from the target if the application
uses the sync manner.
With the uring implementation, after sending the
cmd to the target, we may have the following scenerio:
(1) Firstly receive the incoming pdu(e.g., CapsuleResp pdu, C2hdata pdu)
due to the group polling read event.
(2) Secondly execute the callback function related with NVMe command sending.
This means that the data from the initiator is really sent out to the target,
and the target receives, then sending back the data to the initiator. But the
uring io_uring_cqe event is not handled, thus if we execute
(1) first, it will clean the data structures related with nvme_tcp_req, and the
nvme_tcp_req will be used for other purpose. Then causes wrong behaviour like
the following:
"Rand Write test failed at QD=128 because fio hangs with the following error:
nvme_tcp.c: 971:nvme_tcp_capsule_resp_hdr_handle: *ERROR*:
no tcp_req is found with cid=66 for tqpair=0x7f23d8001710".
And this patch can address this issue.
Signed-off-by: Ziye Yang <ziye.yang@intel.com>
Change-Id: I5043aaa8adf5033d93dedac15f633f0850e0b9f2
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/2818
Community-CI: Mellanox Build Bot
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: Aleksey Marchuk <alexeymar@mellanox.com>
2020-06-09 15:36:04 +00:00
|
|
|
}
|
2020-09-01 08:15:09 +00:00
|
|
|
|
|
|
|
assert(tcp_req->state == NVME_TCP_REQ_ACTIVE);
|
|
|
|
assert(tcp_req->tqpair != NULL);
|
|
|
|
assert(tcp_req->req != NULL);
|
|
|
|
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "complete tcp_req(%p) on tqpair=%p\n", tcp_req, tcp_req->tqpair);
|
2020-09-01 08:15:09 +00:00
|
|
|
|
2020-09-01 08:53:46 +00:00
|
|
|
if (!tcp_req->tqpair->qpair.in_completion_context) {
|
|
|
|
tcp_req->tqpair->async_complete++;
|
|
|
|
}
|
|
|
|
|
2020-09-01 08:15:09 +00:00
|
|
|
/* Cache arguments to be passed to nvme_complete_request since tcp_req can be zeroed when released */
|
|
|
|
memcpy(&cpl, &tcp_req->rsp, sizeof(cpl));
|
|
|
|
user_cb = tcp_req->req->cb_fn;
|
|
|
|
user_cb_arg = tcp_req->req->cb_arg;
|
|
|
|
qpair = tcp_req->req->qpair;
|
|
|
|
req = tcp_req->req;
|
|
|
|
|
|
|
|
TAILQ_REMOVE(&tcp_req->tqpair->outstanding_reqs, tcp_req, link);
|
|
|
|
nvme_tcp_req_put(tcp_req->tqpair, tcp_req);
|
|
|
|
nvme_free_request(tcp_req->req);
|
|
|
|
nvme_complete_request(user_cb, user_cb_arg, qpair, req, &cpl);
|
|
|
|
|
|
|
|
return true;
|
nvme/tcp: Fix nvme_tcp_req free conflict between cmd sending and incoming pdu receiving
This patch tries to solve the out of order
call back handling for cmd sending and the incoming pdu handling.
Normally, the cmd call back will be called before
receving the next PDU from the target if the application
uses the sync manner.
With the uring implementation, after sending the
cmd to the target, we may have the following scenerio:
(1) Firstly receive the incoming pdu(e.g., CapsuleResp pdu, C2hdata pdu)
due to the group polling read event.
(2) Secondly execute the callback function related with NVMe command sending.
This means that the data from the initiator is really sent out to the target,
and the target receives, then sending back the data to the initiator. But the
uring io_uring_cqe event is not handled, thus if we execute
(1) first, it will clean the data structures related with nvme_tcp_req, and the
nvme_tcp_req will be used for other purpose. Then causes wrong behaviour like
the following:
"Rand Write test failed at QD=128 because fio hangs with the following error:
nvme_tcp.c: 971:nvme_tcp_capsule_resp_hdr_handle: *ERROR*:
no tcp_req is found with cid=66 for tqpair=0x7f23d8001710".
And this patch can address this issue.
Signed-off-by: Ziye Yang <ziye.yang@intel.com>
Change-Id: I5043aaa8adf5033d93dedac15f633f0850e0b9f2
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/2818
Community-CI: Mellanox Build Bot
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: Aleksey Marchuk <alexeymar@mellanox.com>
2020-06-09 15:36:04 +00:00
|
|
|
}
|
|
|
|
|
2018-08-02 02:21:45 +00:00
|
|
|
static void
|
|
|
|
nvme_tcp_qpair_cmd_send_complete(void *cb_arg)
|
|
|
|
{
|
nvme/tcp: Fix nvme_tcp_req free conflict between cmd sending and incoming pdu receiving
This patch tries to solve the out of order
call back handling for cmd sending and the incoming pdu handling.
Normally, the cmd call back will be called before
receving the next PDU from the target if the application
uses the sync manner.
With the uring implementation, after sending the
cmd to the target, we may have the following scenerio:
(1) Firstly receive the incoming pdu(e.g., CapsuleResp pdu, C2hdata pdu)
due to the group polling read event.
(2) Secondly execute the callback function related with NVMe command sending.
This means that the data from the initiator is really sent out to the target,
and the target receives, then sending back the data to the initiator. But the
uring io_uring_cqe event is not handled, thus if we execute
(1) first, it will clean the data structures related with nvme_tcp_req, and the
nvme_tcp_req will be used for other purpose. Then causes wrong behaviour like
the following:
"Rand Write test failed at QD=128 because fio hangs with the following error:
nvme_tcp.c: 971:nvme_tcp_capsule_resp_hdr_handle: *ERROR*:
no tcp_req is found with cid=66 for tqpair=0x7f23d8001710".
And this patch can address this issue.
Signed-off-by: Ziye Yang <ziye.yang@intel.com>
Change-Id: I5043aaa8adf5033d93dedac15f633f0850e0b9f2
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/2818
Community-CI: Mellanox Build Bot
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: Aleksey Marchuk <alexeymar@mellanox.com>
2020-06-09 15:36:04 +00:00
|
|
|
struct nvme_tcp_req *tcp_req = cb_arg;
|
|
|
|
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "tcp req %p, cid %u, qid %u\n", tcp_req, tcp_req->cid,
|
2020-09-01 08:15:09 +00:00
|
|
|
tcp_req->tqpair->qpair.id);
|
2020-09-01 07:03:16 +00:00
|
|
|
tcp_req->ordering.bits.send_ack = 1;
|
2020-07-04 16:23:25 +00:00
|
|
|
/* Handle the r2t case */
|
2020-09-01 07:18:33 +00:00
|
|
|
if (spdk_unlikely(tcp_req->ordering.bits.h2c_send_waiting_ack)) {
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "tcp req %p, send H2C data\n", tcp_req);
|
2020-07-04 16:23:25 +00:00
|
|
|
nvme_tcp_send_h2c_data(tcp_req);
|
|
|
|
} else {
|
2020-09-01 08:15:09 +00:00
|
|
|
nvme_tcp_req_complete_safe(tcp_req);
|
2020-07-04 16:23:25 +00:00
|
|
|
}
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
nvme_tcp_qpair_capsule_cmd_send(struct nvme_tcp_qpair *tqpair,
|
|
|
|
struct nvme_tcp_req *tcp_req)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_pdu *pdu;
|
|
|
|
struct spdk_nvme_tcp_cmd *capsule_cmd;
|
|
|
|
uint32_t plen = 0, alignment;
|
|
|
|
uint8_t pdo;
|
|
|
|
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "enter\n");
|
2021-05-25 15:12:20 +00:00
|
|
|
pdu = tcp_req->pdu;
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2019-09-27 21:19:57 +00:00
|
|
|
capsule_cmd = &pdu->hdr.capsule_cmd;
|
2018-08-02 02:21:45 +00:00
|
|
|
capsule_cmd->common.pdu_type = SPDK_NVME_TCP_PDU_TYPE_CAPSULE_CMD;
|
|
|
|
plen = capsule_cmd->common.hlen = sizeof(*capsule_cmd);
|
|
|
|
capsule_cmd->ccsqe = tcp_req->req->cmd;
|
|
|
|
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "capsule_cmd cid=%u on tqpair(%p)\n", tcp_req->req->cmd.cid, tqpair);
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2020-09-01 08:23:25 +00:00
|
|
|
if (tqpair->flags.host_hdgst_enable) {
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "Header digest is enabled for capsule command on tcp_req=%p\n",
|
2018-08-02 02:21:45 +00:00
|
|
|
tcp_req);
|
|
|
|
capsule_cmd->common.flags |= SPDK_NVME_TCP_CH_FLAGS_HDGSTF;
|
|
|
|
plen += SPDK_NVME_TCP_DIGEST_LEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((tcp_req->req->payload_size == 0) || !tcp_req->in_capsule_data) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
pdo = plen;
|
|
|
|
pdu->padding_len = 0;
|
|
|
|
if (tqpair->cpda) {
|
|
|
|
alignment = (tqpair->cpda + 1) << 2;
|
|
|
|
if (alignment > plen) {
|
|
|
|
pdu->padding_len = alignment - plen;
|
|
|
|
pdo = alignment;
|
|
|
|
plen = alignment;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
capsule_cmd->common.pdo = pdo;
|
|
|
|
plen += tcp_req->req->payload_size;
|
2020-09-01 08:23:25 +00:00
|
|
|
if (tqpair->flags.host_ddgst_enable) {
|
2018-08-02 02:21:45 +00:00
|
|
|
capsule_cmd->common.flags |= SPDK_NVME_TCP_CH_FLAGS_DDGSTF;
|
|
|
|
plen += SPDK_NVME_TCP_DIGEST_LEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
tcp_req->datao = 0;
|
2019-05-24 01:51:32 +00:00
|
|
|
nvme_tcp_pdu_set_data_buf(pdu, tcp_req->iov, tcp_req->iovcnt,
|
|
|
|
0, tcp_req->req->payload_size);
|
2018-08-02 02:21:45 +00:00
|
|
|
end:
|
|
|
|
capsule_cmd->common.plen = plen;
|
nvme/tcp: Fix nvme_tcp_req free conflict between cmd sending and incoming pdu receiving
This patch tries to solve the out of order
call back handling for cmd sending and the incoming pdu handling.
Normally, the cmd call back will be called before
receving the next PDU from the target if the application
uses the sync manner.
With the uring implementation, after sending the
cmd to the target, we may have the following scenerio:
(1) Firstly receive the incoming pdu(e.g., CapsuleResp pdu, C2hdata pdu)
due to the group polling read event.
(2) Secondly execute the callback function related with NVMe command sending.
This means that the data from the initiator is really sent out to the target,
and the target receives, then sending back the data to the initiator. But the
uring io_uring_cqe event is not handled, thus if we execute
(1) first, it will clean the data structures related with nvme_tcp_req, and the
nvme_tcp_req will be used for other purpose. Then causes wrong behaviour like
the following:
"Rand Write test failed at QD=128 because fio hangs with the following error:
nvme_tcp.c: 971:nvme_tcp_capsule_resp_hdr_handle: *ERROR*:
no tcp_req is found with cid=66 for tqpair=0x7f23d8001710".
And this patch can address this issue.
Signed-off-by: Ziye Yang <ziye.yang@intel.com>
Change-Id: I5043aaa8adf5033d93dedac15f633f0850e0b9f2
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/2818
Community-CI: Mellanox Build Bot
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: Aleksey Marchuk <alexeymar@mellanox.com>
2020-06-09 15:36:04 +00:00
|
|
|
return nvme_tcp_qpair_write_pdu(tqpair, pdu, nvme_tcp_qpair_cmd_send_complete, tcp_req);
|
2018-08-02 02:21:45 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-02-10 19:13:06 +00:00
|
|
|
static int
|
2018-08-02 02:21:45 +00:00
|
|
|
nvme_tcp_qpair_submit_request(struct spdk_nvme_qpair *qpair,
|
|
|
|
struct nvme_request *req)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_qpair *tqpair;
|
|
|
|
struct nvme_tcp_req *tcp_req;
|
|
|
|
|
|
|
|
tqpair = nvme_tcp_qpair(qpair);
|
|
|
|
assert(tqpair != NULL);
|
|
|
|
assert(req != NULL);
|
|
|
|
|
|
|
|
tcp_req = nvme_tcp_req_get(tqpair);
|
2019-05-09 17:40:12 +00:00
|
|
|
if (!tcp_req) {
|
2021-07-19 08:57:48 +00:00
|
|
|
tqpair->stats->queued_requests++;
|
2019-09-27 20:52:21 +00:00
|
|
|
/* Inform the upper layer to try again later. */
|
|
|
|
return -EAGAIN;
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (nvme_tcp_req_init(tqpair, req, tcp_req)) {
|
|
|
|
SPDK_ERRLOG("nvme_tcp_req_init() failed\n");
|
2020-06-29 14:32:45 +00:00
|
|
|
TAILQ_REMOVE(&tcp_req->tqpair->outstanding_reqs, tcp_req, link);
|
2018-08-02 02:21:45 +00:00
|
|
|
nvme_tcp_req_put(tqpair, tcp_req);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nvme_tcp_qpair_capsule_cmd_send(tqpair, tcp_req);
|
|
|
|
}
|
|
|
|
|
2020-02-10 19:13:06 +00:00
|
|
|
static int
|
2018-08-02 02:21:45 +00:00
|
|
|
nvme_tcp_qpair_reset(struct spdk_nvme_qpair *qpair)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-02-14 10:11:56 +00:00
|
|
|
static void
|
2020-06-30 16:28:27 +00:00
|
|
|
nvme_tcp_req_complete(struct nvme_tcp_req *tcp_req,
|
2019-02-14 10:11:56 +00:00
|
|
|
struct spdk_nvme_cpl *rsp)
|
|
|
|
{
|
2020-06-30 16:28:27 +00:00
|
|
|
struct nvme_request *req;
|
|
|
|
|
|
|
|
assert(tcp_req->req != NULL);
|
|
|
|
req = tcp_req->req;
|
|
|
|
|
2020-06-29 14:32:45 +00:00
|
|
|
TAILQ_REMOVE(&tcp_req->tqpair->outstanding_reqs, tcp_req, link);
|
2019-05-21 10:06:42 +00:00
|
|
|
nvme_complete_request(req->cb_fn, req->cb_arg, req->qpair, req, rsp);
|
2019-02-14 10:11:56 +00:00
|
|
|
nvme_free_request(req);
|
|
|
|
}
|
|
|
|
|
2020-02-10 19:13:06 +00:00
|
|
|
static void
|
2019-05-06 23:14:24 +00:00
|
|
|
nvme_tcp_qpair_abort_reqs(struct spdk_nvme_qpair *qpair, uint32_t dnr)
|
2018-08-02 02:21:45 +00:00
|
|
|
{
|
2019-02-14 10:11:56 +00:00
|
|
|
struct nvme_tcp_req *tcp_req, *tmp;
|
|
|
|
struct spdk_nvme_cpl cpl;
|
|
|
|
struct nvme_tcp_qpair *tqpair = nvme_tcp_qpair(qpair);
|
|
|
|
|
|
|
|
cpl.status.sc = SPDK_NVME_SC_ABORTED_SQ_DELETION;
|
|
|
|
cpl.status.sct = SPDK_NVME_SCT_GENERIC;
|
2019-05-06 23:14:24 +00:00
|
|
|
cpl.status.dnr = dnr;
|
2019-02-14 10:11:56 +00:00
|
|
|
|
|
|
|
TAILQ_FOREACH_SAFE(tcp_req, &tqpair->outstanding_reqs, link, tmp) {
|
2020-06-30 16:28:27 +00:00
|
|
|
nvme_tcp_req_complete(tcp_req, &cpl);
|
2019-02-14 10:11:56 +00:00
|
|
|
nvme_tcp_req_put(tqpair, tcp_req);
|
|
|
|
}
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nvme_tcp_qpair_set_recv_state(struct nvme_tcp_qpair *tqpair,
|
|
|
|
enum nvme_tcp_pdu_recv_state state)
|
|
|
|
{
|
|
|
|
if (tqpair->recv_state == state) {
|
|
|
|
SPDK_ERRLOG("The recv state of tqpair=%p is same with the state(%d) to be set\n",
|
|
|
|
tqpair, state);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
tqpair->recv_state = state;
|
|
|
|
switch (state) {
|
|
|
|
case NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_READY:
|
|
|
|
case NVME_TCP_PDU_RECV_STATE_ERROR:
|
2021-05-06 14:13:11 +00:00
|
|
|
memset(tqpair->recv_pdu, 0, sizeof(struct nvme_tcp_pdu));
|
2018-08-02 02:21:45 +00:00
|
|
|
break;
|
|
|
|
case NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_CH:
|
|
|
|
case NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_PSH:
|
|
|
|
case NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_PAYLOAD:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nvme_tcp_qpair_send_h2c_term_req_complete(void *cb_arg)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_qpair *tqpair = cb_arg;
|
|
|
|
|
|
|
|
tqpair->state = NVME_TCP_QPAIR_STATE_EXITING;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nvme_tcp_qpair_send_h2c_term_req(struct nvme_tcp_qpair *tqpair, struct nvme_tcp_pdu *pdu,
|
|
|
|
enum spdk_nvme_tcp_term_req_fes fes, uint32_t error_offset)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_pdu *rsp_pdu;
|
|
|
|
struct spdk_nvme_tcp_term_req_hdr *h2c_term_req;
|
|
|
|
uint32_t h2c_term_req_hdr_len = sizeof(*h2c_term_req);
|
2018-12-05 13:48:28 +00:00
|
|
|
uint8_t copy_len;
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2020-07-30 11:43:51 +00:00
|
|
|
rsp_pdu = tqpair->send_pdu;
|
2018-08-02 02:21:45 +00:00
|
|
|
memset(rsp_pdu, 0, sizeof(*rsp_pdu));
|
2019-09-27 21:19:57 +00:00
|
|
|
h2c_term_req = &rsp_pdu->hdr.term_req;
|
2018-08-02 02:21:45 +00:00
|
|
|
h2c_term_req->common.pdu_type = SPDK_NVME_TCP_PDU_TYPE_H2C_TERM_REQ;
|
|
|
|
h2c_term_req->common.hlen = h2c_term_req_hdr_len;
|
|
|
|
|
|
|
|
if ((fes == SPDK_NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD) ||
|
|
|
|
(fes == SPDK_NVME_TCP_TERM_REQ_FES_INVALID_DATA_UNSUPPORTED_PARAMETER)) {
|
|
|
|
DSET32(&h2c_term_req->fei, error_offset);
|
|
|
|
}
|
|
|
|
|
2019-09-27 21:19:57 +00:00
|
|
|
copy_len = pdu->hdr.common.hlen;
|
2018-12-05 13:48:28 +00:00
|
|
|
if (copy_len > SPDK_NVME_TCP_TERM_REQ_ERROR_DATA_MAX_SIZE) {
|
|
|
|
copy_len = SPDK_NVME_TCP_TERM_REQ_ERROR_DATA_MAX_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy the error info into the buffer */
|
2019-09-27 21:19:57 +00:00
|
|
|
memcpy((uint8_t *)rsp_pdu->hdr.raw + h2c_term_req_hdr_len, pdu->hdr.raw, copy_len);
|
|
|
|
nvme_tcp_pdu_set_data(rsp_pdu, (uint8_t *)rsp_pdu->hdr.raw + h2c_term_req_hdr_len, copy_len);
|
2018-12-05 13:48:28 +00:00
|
|
|
|
|
|
|
/* Contain the header len of the wrong received pdu */
|
|
|
|
h2c_term_req->common.plen = h2c_term_req->common.hlen + copy_len;
|
2018-08-02 02:21:45 +00:00
|
|
|
nvme_tcp_qpair_set_recv_state(tqpair, NVME_TCP_PDU_RECV_STATE_ERROR);
|
2021-03-04 01:03:03 +00:00
|
|
|
nvme_tcp_qpair_write_pdu(tqpair, rsp_pdu, nvme_tcp_qpair_send_h2c_term_req_complete, tqpair);
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
2021-06-08 19:33:58 +00:00
|
|
|
static bool
|
|
|
|
nvme_tcp_qpair_recv_state_valid(struct nvme_tcp_qpair *tqpair)
|
|
|
|
{
|
|
|
|
switch (tqpair->state) {
|
|
|
|
case NVME_TCP_QPAIR_STATE_FABRIC_CONNECT_SEND:
|
|
|
|
case NVME_TCP_QPAIR_STATE_FABRIC_CONNECT_POLL:
|
|
|
|
case NVME_TCP_QPAIR_STATE_RUNNING:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-02 02:21:45 +00:00
|
|
|
static void
|
|
|
|
nvme_tcp_pdu_ch_handle(struct nvme_tcp_qpair *tqpair)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_pdu *pdu;
|
|
|
|
uint32_t error_offset = 0;
|
|
|
|
enum spdk_nvme_tcp_term_req_fes fes;
|
|
|
|
uint32_t expected_hlen, hd_len = 0;
|
|
|
|
bool plen_error = false;
|
|
|
|
|
2021-05-06 14:13:11 +00:00
|
|
|
pdu = tqpair->recv_pdu;
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "pdu type = %d\n", pdu->hdr.common.pdu_type);
|
2019-09-27 21:19:57 +00:00
|
|
|
if (pdu->hdr.common.pdu_type == SPDK_NVME_TCP_PDU_TYPE_IC_RESP) {
|
2018-08-02 02:21:45 +00:00
|
|
|
if (tqpair->state != NVME_TCP_QPAIR_STATE_INVALID) {
|
|
|
|
SPDK_ERRLOG("Already received IC_RESP PDU, and we should reject this pdu=%p\n", pdu);
|
|
|
|
fes = SPDK_NVME_TCP_TERM_REQ_FES_PDU_SEQUENCE_ERROR;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
expected_hlen = sizeof(struct spdk_nvme_tcp_ic_resp);
|
2019-09-27 21:19:57 +00:00
|
|
|
if (pdu->hdr.common.plen != expected_hlen) {
|
2018-08-02 02:21:45 +00:00
|
|
|
plen_error = true;
|
|
|
|
}
|
|
|
|
} else {
|
2021-06-08 19:33:58 +00:00
|
|
|
if (spdk_unlikely(!nvme_tcp_qpair_recv_state_valid(tqpair))) {
|
2021-11-25 01:40:58 +00:00
|
|
|
SPDK_ERRLOG("The TCP/IP tqpair connection is not negotiated\n");
|
2018-08-02 02:21:45 +00:00
|
|
|
fes = SPDK_NVME_TCP_TERM_REQ_FES_PDU_SEQUENCE_ERROR;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2019-09-27 21:19:57 +00:00
|
|
|
switch (pdu->hdr.common.pdu_type) {
|
2018-08-02 02:21:45 +00:00
|
|
|
case SPDK_NVME_TCP_PDU_TYPE_CAPSULE_RESP:
|
|
|
|
expected_hlen = sizeof(struct spdk_nvme_tcp_rsp);
|
2019-09-27 21:19:57 +00:00
|
|
|
if (pdu->hdr.common.flags & SPDK_NVME_TCP_CH_FLAGS_HDGSTF) {
|
2018-08-02 02:21:45 +00:00
|
|
|
hd_len = SPDK_NVME_TCP_DIGEST_LEN;
|
|
|
|
}
|
|
|
|
|
2019-09-27 21:19:57 +00:00
|
|
|
if (pdu->hdr.common.plen != (expected_hlen + hd_len)) {
|
2018-08-02 02:21:45 +00:00
|
|
|
plen_error = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SPDK_NVME_TCP_PDU_TYPE_C2H_DATA:
|
|
|
|
expected_hlen = sizeof(struct spdk_nvme_tcp_c2h_data_hdr);
|
2019-09-27 21:19:57 +00:00
|
|
|
if (pdu->hdr.common.plen < pdu->hdr.common.pdo) {
|
2018-08-02 02:21:45 +00:00
|
|
|
plen_error = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SPDK_NVME_TCP_PDU_TYPE_C2H_TERM_REQ:
|
|
|
|
expected_hlen = sizeof(struct spdk_nvme_tcp_term_req_hdr);
|
2019-09-27 21:19:57 +00:00
|
|
|
if ((pdu->hdr.common.plen <= expected_hlen) ||
|
|
|
|
(pdu->hdr.common.plen > SPDK_NVME_TCP_TERM_REQ_PDU_MAX_SIZE)) {
|
2018-08-02 02:21:45 +00:00
|
|
|
plen_error = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SPDK_NVME_TCP_PDU_TYPE_R2T:
|
|
|
|
expected_hlen = sizeof(struct spdk_nvme_tcp_r2t_hdr);
|
2019-09-27 21:19:57 +00:00
|
|
|
if (pdu->hdr.common.flags & SPDK_NVME_TCP_CH_FLAGS_HDGSTF) {
|
2018-08-02 02:21:45 +00:00
|
|
|
hd_len = SPDK_NVME_TCP_DIGEST_LEN;
|
|
|
|
}
|
|
|
|
|
2019-09-27 21:19:57 +00:00
|
|
|
if (pdu->hdr.common.plen != (expected_hlen + hd_len)) {
|
2018-08-02 02:21:45 +00:00
|
|
|
plen_error = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2021-05-06 14:13:11 +00:00
|
|
|
SPDK_ERRLOG("Unexpected PDU type 0x%02x\n", tqpair->recv_pdu->hdr.common.pdu_type);
|
2018-08-02 02:21:45 +00:00
|
|
|
fes = SPDK_NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
|
|
|
|
error_offset = offsetof(struct spdk_nvme_tcp_common_pdu_hdr, pdu_type);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-27 21:19:57 +00:00
|
|
|
if (pdu->hdr.common.hlen != expected_hlen) {
|
2018-08-02 02:21:45 +00:00
|
|
|
SPDK_ERRLOG("Expected PDU header length %u, got %u\n",
|
2019-09-27 21:19:57 +00:00
|
|
|
expected_hlen, pdu->hdr.common.hlen);
|
2018-08-02 02:21:45 +00:00
|
|
|
fes = SPDK_NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
|
|
|
|
error_offset = offsetof(struct spdk_nvme_tcp_common_pdu_hdr, hlen);
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
} else if (plen_error) {
|
|
|
|
fes = SPDK_NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
|
|
|
|
error_offset = offsetof(struct spdk_nvme_tcp_common_pdu_hdr, plen);
|
|
|
|
goto err;
|
|
|
|
} else {
|
|
|
|
nvme_tcp_qpair_set_recv_state(tqpair, NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_PSH);
|
2021-05-06 14:13:11 +00:00
|
|
|
nvme_tcp_pdu_calc_psh_len(tqpair->recv_pdu, tqpair->flags.host_hdgst_enable);
|
2018-08-02 02:21:45 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
err:
|
|
|
|
nvme_tcp_qpair_send_h2c_term_req(tqpair, pdu, fes, error_offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct nvme_tcp_req *
|
|
|
|
get_nvme_active_req_by_cid(struct nvme_tcp_qpair *tqpair, uint32_t cid)
|
|
|
|
{
|
|
|
|
assert(tqpair != NULL);
|
|
|
|
if ((cid >= tqpair->num_entries) || (tqpair->tcp_reqs[cid].state == NVME_TCP_REQ_FREE)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return &tqpair->tcp_reqs[cid];
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nvme_tcp_c2h_data_payload_handle(struct nvme_tcp_qpair *tqpair,
|
|
|
|
struct nvme_tcp_pdu *pdu, uint32_t *reaped)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_req *tcp_req;
|
|
|
|
struct spdk_nvme_tcp_c2h_data_hdr *c2h_data;
|
|
|
|
uint8_t flags;
|
|
|
|
|
2019-10-01 20:45:35 +00:00
|
|
|
tcp_req = pdu->req;
|
2018-08-02 02:21:45 +00:00
|
|
|
assert(tcp_req != NULL);
|
|
|
|
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "enter\n");
|
2019-09-27 21:19:57 +00:00
|
|
|
c2h_data = &pdu->hdr.c2h_data;
|
2018-08-02 02:21:45 +00:00
|
|
|
tcp_req->datao += pdu->data_len;
|
|
|
|
flags = c2h_data->common.flags;
|
|
|
|
|
2021-08-17 12:48:07 +00:00
|
|
|
if (flags & SPDK_NVME_TCP_C2H_DATA_FLAGS_LAST_PDU) {
|
2018-08-02 02:21:45 +00:00
|
|
|
if (tcp_req->datao == tcp_req->req->payload_size) {
|
2020-09-01 08:15:09 +00:00
|
|
|
tcp_req->rsp.status.p = 0;
|
2018-08-02 02:21:45 +00:00
|
|
|
} else {
|
2020-09-01 08:15:09 +00:00
|
|
|
tcp_req->rsp.status.p = 1;
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
2020-09-01 08:15:09 +00:00
|
|
|
tcp_req->rsp.cid = tcp_req->cid;
|
|
|
|
tcp_req->rsp.sqid = tqpair->qpair.id;
|
2021-08-17 12:48:07 +00:00
|
|
|
if (flags & SPDK_NVME_TCP_C2H_DATA_FLAGS_SUCCESS) {
|
|
|
|
tcp_req->ordering.bits.data_recv = 1;
|
|
|
|
if (nvme_tcp_req_complete_safe(tcp_req)) {
|
|
|
|
(*reaped)++;
|
|
|
|
}
|
2020-07-08 11:38:58 +00:00
|
|
|
}
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *spdk_nvme_tcp_term_req_fes_str[] = {
|
|
|
|
"Invalid PDU Header Field",
|
|
|
|
"PDU Sequence Error",
|
|
|
|
"Header Digest Error",
|
|
|
|
"Data Transfer Out of Range",
|
|
|
|
"Data Transfer Limit Exceeded",
|
|
|
|
"Unsupported parameter",
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
nvme_tcp_c2h_term_req_dump(struct spdk_nvme_tcp_term_req_hdr *c2h_term_req)
|
|
|
|
{
|
|
|
|
SPDK_ERRLOG("Error info of pdu(%p): %s\n", c2h_term_req,
|
|
|
|
spdk_nvme_tcp_term_req_fes_str[c2h_term_req->fes]);
|
|
|
|
if ((c2h_term_req->fes == SPDK_NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD) ||
|
|
|
|
(c2h_term_req->fes == SPDK_NVME_TCP_TERM_REQ_FES_INVALID_DATA_UNSUPPORTED_PARAMETER)) {
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "The offset from the start of the PDU header is %u\n",
|
2018-08-02 02:21:45 +00:00
|
|
|
DGET32(c2h_term_req->fei));
|
|
|
|
}
|
|
|
|
/* we may also need to dump some other info here */
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nvme_tcp_c2h_term_req_payload_handle(struct nvme_tcp_qpair *tqpair,
|
|
|
|
struct nvme_tcp_pdu *pdu)
|
|
|
|
{
|
2019-09-27 21:19:57 +00:00
|
|
|
nvme_tcp_c2h_term_req_dump(&pdu->hdr.term_req);
|
2018-08-02 02:21:45 +00:00
|
|
|
nvme_tcp_qpair_set_recv_state(tqpair, NVME_TCP_PDU_RECV_STATE_ERROR);
|
|
|
|
}
|
|
|
|
|
2021-05-07 10:34:35 +00:00
|
|
|
static void
|
|
|
|
_nvme_tcp_pdu_payload_handle(struct nvme_tcp_qpair *tqpair, uint32_t *reaped)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_pdu *pdu;
|
|
|
|
|
|
|
|
assert(tqpair != NULL);
|
|
|
|
pdu = tqpair->recv_pdu;
|
|
|
|
|
|
|
|
switch (pdu->hdr.common.pdu_type) {
|
|
|
|
case SPDK_NVME_TCP_PDU_TYPE_C2H_DATA:
|
|
|
|
nvme_tcp_c2h_data_payload_handle(tqpair, pdu, reaped);
|
2021-05-04 23:06:47 +00:00
|
|
|
nvme_tcp_qpair_set_recv_state(tqpair, NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_READY);
|
2021-05-07 10:34:35 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SPDK_NVME_TCP_PDU_TYPE_C2H_TERM_REQ:
|
|
|
|
nvme_tcp_c2h_term_req_payload_handle(tqpair, pdu);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* The code should not go to here */
|
|
|
|
SPDK_ERRLOG("The code should not go to here\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-04 23:06:47 +00:00
|
|
|
static void
|
|
|
|
tcp_data_recv_crc32_done(void *cb_arg, int status)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_req *tcp_req = cb_arg;
|
|
|
|
struct nvme_tcp_pdu *pdu;
|
|
|
|
struct nvme_tcp_qpair *tqpair;
|
|
|
|
int rc;
|
|
|
|
struct nvme_tcp_poll_group *pgroup;
|
|
|
|
int dummy_reaped = 0;
|
|
|
|
|
|
|
|
pdu = tcp_req->pdu;
|
|
|
|
assert(pdu != NULL);
|
|
|
|
|
|
|
|
tqpair = tcp_req->tqpair;
|
|
|
|
assert(tqpair != NULL);
|
|
|
|
|
2021-09-19 22:06:37 +00:00
|
|
|
if (tqpair->qpair.poll_group && !tqpair->needs_poll) {
|
2021-05-04 23:06:47 +00:00
|
|
|
pgroup = nvme_tcp_poll_group(tqpair->qpair.poll_group);
|
|
|
|
TAILQ_INSERT_TAIL(&pgroup->needs_poll, tqpair, link);
|
|
|
|
tqpair->needs_poll = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (spdk_unlikely(status)) {
|
|
|
|
SPDK_ERRLOG("Failed to compute the data digest for pdu =%p\n", pdu);
|
|
|
|
tcp_req->rsp.status.sc = SPDK_NVME_SC_COMMAND_TRANSIENT_TRANSPORT_ERROR;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
pdu->data_digest_crc32 ^= SPDK_CRC32C_XOR;
|
|
|
|
rc = MATCH_DIGEST_WORD(pdu->data_digest, pdu->data_digest_crc32);
|
|
|
|
if (rc == 0) {
|
|
|
|
SPDK_ERRLOG("data digest error on tqpair=(%p) with pdu=%p\n", tqpair, pdu);
|
|
|
|
tcp_req->rsp.status.sc = SPDK_NVME_SC_COMMAND_TRANSIENT_TRANSPORT_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
end:
|
|
|
|
tcp_req->pdu_in_use = false;
|
|
|
|
nvme_tcp_c2h_data_payload_handle(tqpair, tcp_req->pdu, &dummy_reaped);
|
|
|
|
}
|
|
|
|
|
2018-08-02 02:21:45 +00:00
|
|
|
static void
|
|
|
|
nvme_tcp_pdu_payload_handle(struct nvme_tcp_qpair *tqpair,
|
|
|
|
uint32_t *reaped)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
struct nvme_tcp_pdu *pdu;
|
2021-05-07 13:33:40 +00:00
|
|
|
uint32_t crc32c;
|
2021-05-04 23:06:47 +00:00
|
|
|
struct nvme_tcp_poll_group *tgroup;
|
2021-05-07 13:33:40 +00:00
|
|
|
struct nvme_tcp_req *tcp_req;
|
2018-08-02 02:21:45 +00:00
|
|
|
|
|
|
|
assert(tqpair->recv_state == NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_PAYLOAD);
|
2021-05-06 14:13:11 +00:00
|
|
|
pdu = tqpair->recv_pdu;
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "enter\n");
|
2018-08-02 02:21:45 +00:00
|
|
|
|
nvme/tcp: Fix tcp_req->datao calculation issue.
When data digest is enabled for a nvme tcp qpair, we can use accel_fw
to calculate the data crc32c. Then if there are multiple
c2h pdus are coming, we can use both CPU resource directly
and accel_fw framework to caculate the checksum. Then the datao value compare
will not match since we will not update "datao" in the pdu coming order.
For example, if we receive 4 pdus, named as A, B, C, D.
offset data_len (in bytes)
A: 0 8192
B: 8192 4096
C: 12288 8192
D: 20480 4096
For receving the pdu, we hope that we can continue exeution even if
we use the offloading engine in accel_fw. Then in this situation,
if Pdu(C) is offloaded by accel_fw. Then our logic will continue receving
PDU(D). And according to the logic in our code, this time we leverage CPU
to calculate crc32c (Because we only have one active pdu to receive data).
Then we find the expected data offset is still 12288. Because "datao" in tcp_req will
only be updated after calling nvme_tcp_c2h_data_payload_handle function. So
while we enter nvme_tcp_c2h_data_hdr_handle function, we will find the
expected datao value is not as expected compared with the data offset value
contained in Pdu(D).
So the solution is that we create a new variable "expected_datao"
in tcp_req to do the comparation because we want to comply with the tp8000 spec
and do the offset check.
We still need use "datao" to count whether we receive the whole data or not.
So we cannot reuse "datao" variable in an early way. Otherwise, we will
release tcp_req structure early and cause another bug.
PS: This bug was not found early because previously the sw path in accel_fw
directly calculated the crc32c and called the user callback. Now we use a list and the
poller to handle, then it triggers this issue. Definitely, it will be much easier to
trigger this issue if we use real hardware engine.
Fixes #2098
Signed-off-by: Ziye Yang <ziye.yang@intel.com>
Change-Id: I10f5938a6342028d08d90820b2c14e4260134d77
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/9612
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Changpeng Liu <changpeng.liu@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: GangCao <gang.cao@intel.com>
Community-CI: Broadcom CI <spdk-ci.pdl@broadcom.com>
Community-CI: Mellanox Build Bot
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
2021-09-24 15:34:23 +00:00
|
|
|
tcp_req = pdu->req;
|
|
|
|
/* Increase the expected data offset */
|
|
|
|
tcp_req->expected_datao += pdu->data_len;
|
|
|
|
|
2018-08-02 02:21:45 +00:00
|
|
|
/* check data digest if need */
|
2019-03-11 18:21:56 +00:00
|
|
|
if (pdu->ddgst_enable) {
|
2021-05-04 23:06:47 +00:00
|
|
|
tgroup = nvme_tcp_poll_group(tqpair->qpair.poll_group);
|
|
|
|
/* Only suport this limitated case for the first step */
|
|
|
|
if ((nvme_qpair_get_state(&tqpair->qpair) >= NVME_QPAIR_CONNECTED) &&
|
|
|
|
(tgroup != NULL && tgroup->group.group->accel_fn_table.submit_accel_crc32c) &&
|
|
|
|
spdk_likely(!pdu->dif_ctx && (pdu->data_len % SPDK_NVME_TCP_DIGEST_ALIGNMENT == 0)
|
|
|
|
&& !tcp_req->pdu_in_use)) {
|
|
|
|
|
|
|
|
tcp_req->pdu_in_use = true;
|
|
|
|
tcp_req->pdu->hdr = pdu->hdr;
|
|
|
|
tcp_req->pdu->req = tcp_req;
|
|
|
|
memcpy(tcp_req->pdu->data_digest, pdu->data_digest, sizeof(pdu->data_digest));
|
|
|
|
memcpy(tcp_req->pdu->data_iov, pdu->data_iov, sizeof(pdu->data_iov[0]) * pdu->data_iovcnt);
|
|
|
|
tcp_req->pdu->data_iovcnt = pdu->data_iovcnt;
|
|
|
|
tcp_req->pdu->data_len = pdu->data_len;
|
|
|
|
|
|
|
|
nvme_tcp_qpair_set_recv_state(tqpair, NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_READY);
|
|
|
|
tgroup->group.group->accel_fn_table.submit_accel_crc32c(tgroup->group.group->ctx,
|
|
|
|
&tcp_req->pdu->data_digest_crc32, tcp_req->pdu->data_iov,
|
|
|
|
tcp_req->pdu->data_iovcnt, 0, tcp_data_recv_crc32_done, tcp_req);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-08-02 02:21:45 +00:00
|
|
|
crc32c = nvme_tcp_pdu_calc_data_digest(pdu);
|
|
|
|
rc = MATCH_DIGEST_WORD(pdu->data_digest, crc32c);
|
|
|
|
if (rc == 0) {
|
|
|
|
SPDK_ERRLOG("data digest error on tqpair=(%p) with pdu=%p\n", tqpair, pdu);
|
2021-05-07 13:33:40 +00:00
|
|
|
tcp_req = pdu->req;
|
|
|
|
assert(tcp_req != NULL);
|
|
|
|
tcp_req->rsp.status.sc = SPDK_NVME_SC_COMMAND_TRANSIENT_TRANSPORT_ERROR;
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-07 10:34:35 +00:00
|
|
|
_nvme_tcp_pdu_payload_handle(tqpair, reaped);
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nvme_tcp_send_icreq_complete(void *cb_arg)
|
|
|
|
{
|
2020-09-01 08:33:27 +00:00
|
|
|
struct nvme_tcp_qpair *tqpair = cb_arg;
|
|
|
|
|
2020-10-29 19:31:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "Complete the icreq send for tqpair=%p %u\n", tqpair, tqpair->qpair.id);
|
2020-09-01 08:33:27 +00:00
|
|
|
|
|
|
|
tqpair->flags.icreq_send_ack = true;
|
|
|
|
|
|
|
|
if (tqpair->state == NVME_TCP_QPAIR_STATE_INITIALIZING) {
|
2021-11-25 01:40:58 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "tqpair %p %u, finalize icresp\n", tqpair, tqpair->qpair.id);
|
2021-06-08 19:33:58 +00:00
|
|
|
tqpair->state = NVME_TCP_QPAIR_STATE_FABRIC_CONNECT_SEND;
|
2020-09-01 08:33:27 +00:00
|
|
|
}
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nvme_tcp_icresp_handle(struct nvme_tcp_qpair *tqpair,
|
|
|
|
struct nvme_tcp_pdu *pdu)
|
|
|
|
{
|
2019-09-27 21:19:57 +00:00
|
|
|
struct spdk_nvme_tcp_ic_resp *ic_resp = &pdu->hdr.ic_resp;
|
2018-08-02 02:21:45 +00:00
|
|
|
uint32_t error_offset = 0;
|
|
|
|
enum spdk_nvme_tcp_term_req_fes fes;
|
2020-04-01 17:49:25 +00:00
|
|
|
int recv_buf_size;
|
2018-08-02 02:21:45 +00:00
|
|
|
|
|
|
|
/* Only PFV 0 is defined currently */
|
|
|
|
if (ic_resp->pfv != 0) {
|
|
|
|
SPDK_ERRLOG("Expected ICResp PFV %u, got %u\n", 0u, ic_resp->pfv);
|
|
|
|
fes = SPDK_NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
|
|
|
|
error_offset = offsetof(struct spdk_nvme_tcp_ic_resp, pfv);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ic_resp->maxh2cdata < NVME_TCP_PDU_H2C_MIN_DATA_SIZE) {
|
|
|
|
SPDK_ERRLOG("Expected ICResp maxh2cdata >=%u, got %u\n", NVME_TCP_PDU_H2C_MIN_DATA_SIZE,
|
|
|
|
ic_resp->maxh2cdata);
|
|
|
|
fes = SPDK_NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
|
|
|
|
error_offset = offsetof(struct spdk_nvme_tcp_ic_resp, maxh2cdata);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
tqpair->maxh2cdata = ic_resp->maxh2cdata;
|
|
|
|
|
|
|
|
if (ic_resp->cpda > SPDK_NVME_TCP_CPDA_MAX) {
|
|
|
|
SPDK_ERRLOG("Expected ICResp cpda <=%u, got %u\n", SPDK_NVME_TCP_CPDA_MAX, ic_resp->cpda);
|
|
|
|
fes = SPDK_NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
|
|
|
|
error_offset = offsetof(struct spdk_nvme_tcp_ic_resp, cpda);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
tqpair->cpda = ic_resp->cpda;
|
|
|
|
|
2020-09-01 08:23:25 +00:00
|
|
|
tqpair->flags.host_hdgst_enable = ic_resp->dgst.bits.hdgst_enable ? true : false;
|
|
|
|
tqpair->flags.host_ddgst_enable = ic_resp->dgst.bits.ddgst_enable ? true : false;
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "host_hdgst_enable: %u\n", tqpair->flags.host_hdgst_enable);
|
|
|
|
SPDK_DEBUGLOG(nvme, "host_ddgst_enable: %u\n", tqpair->flags.host_ddgst_enable);
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2020-04-01 17:49:25 +00:00
|
|
|
/* Now that we know whether digests are enabled, properly size the receive buffer to
|
2020-07-10 17:03:06 +00:00
|
|
|
* handle several incoming 4K read commands according to SPDK_NVMF_TCP_RECV_BUF_SIZE_FACTOR
|
|
|
|
* parameter. */
|
|
|
|
recv_buf_size = 0x1000 + sizeof(struct spdk_nvme_tcp_c2h_data_hdr);
|
2020-04-01 17:49:25 +00:00
|
|
|
|
2020-09-01 08:23:25 +00:00
|
|
|
if (tqpair->flags.host_hdgst_enable) {
|
2020-04-01 17:49:25 +00:00
|
|
|
recv_buf_size += SPDK_NVME_TCP_DIGEST_LEN;
|
|
|
|
}
|
|
|
|
|
2020-09-01 08:23:25 +00:00
|
|
|
if (tqpair->flags.host_ddgst_enable) {
|
2020-04-01 17:49:25 +00:00
|
|
|
recv_buf_size += SPDK_NVME_TCP_DIGEST_LEN;
|
|
|
|
}
|
|
|
|
|
2020-07-10 17:03:06 +00:00
|
|
|
if (spdk_sock_set_recvbuf(tqpair->sock, recv_buf_size * SPDK_NVMF_TCP_RECV_BUF_SIZE_FACTOR) < 0) {
|
2020-04-01 17:49:25 +00:00
|
|
|
SPDK_WARNLOG("Unable to allocate enough memory for receive buffer on tqpair=%p with size=%d\n",
|
|
|
|
tqpair,
|
|
|
|
recv_buf_size);
|
|
|
|
/* Not fatal. */
|
|
|
|
}
|
|
|
|
|
2018-08-02 02:21:45 +00:00
|
|
|
nvme_tcp_qpair_set_recv_state(tqpair, NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_READY);
|
2020-10-29 19:31:29 +00:00
|
|
|
|
|
|
|
if (!tqpair->flags.icreq_send_ack) {
|
|
|
|
tqpair->state = NVME_TCP_QPAIR_STATE_INITIALIZING;
|
|
|
|
SPDK_DEBUGLOG(nvme, "tqpair %p %u, waiting icreq ack\n", tqpair, tqpair->qpair.id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-06-08 19:33:58 +00:00
|
|
|
tqpair->state = NVME_TCP_QPAIR_STATE_FABRIC_CONNECT_SEND;
|
2018-08-02 02:21:45 +00:00
|
|
|
return;
|
|
|
|
end:
|
|
|
|
nvme_tcp_qpair_send_h2c_term_req(tqpair, pdu, fes, error_offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nvme_tcp_capsule_resp_hdr_handle(struct nvme_tcp_qpair *tqpair, struct nvme_tcp_pdu *pdu,
|
|
|
|
uint32_t *reaped)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_req *tcp_req;
|
2019-09-27 21:19:57 +00:00
|
|
|
struct spdk_nvme_tcp_rsp *capsule_resp = &pdu->hdr.capsule_resp;
|
2018-08-02 02:21:45 +00:00
|
|
|
uint32_t cid, error_offset = 0;
|
|
|
|
enum spdk_nvme_tcp_term_req_fes fes;
|
|
|
|
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "enter\n");
|
2020-09-01 08:15:09 +00:00
|
|
|
cid = capsule_resp->rccqe.cid;
|
2018-08-02 02:21:45 +00:00
|
|
|
tcp_req = get_nvme_active_req_by_cid(tqpair, cid);
|
2020-09-01 08:15:09 +00:00
|
|
|
|
2018-08-02 02:21:45 +00:00
|
|
|
if (!tcp_req) {
|
|
|
|
SPDK_ERRLOG("no tcp_req is found with cid=%u for tqpair=%p\n", cid, tqpair);
|
|
|
|
fes = SPDK_NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
|
|
|
|
error_offset = offsetof(struct spdk_nvme_tcp_rsp, rccqe);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2020-09-01 08:15:09 +00:00
|
|
|
assert(tcp_req->req != NULL);
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2020-09-01 08:15:09 +00:00
|
|
|
tcp_req->rsp = capsule_resp->rccqe;
|
2020-09-01 07:03:16 +00:00
|
|
|
tcp_req->ordering.bits.data_recv = 1;
|
nvme/tcp: Fix nvme_tcp_req free conflict between cmd sending and incoming pdu receiving
This patch tries to solve the out of order
call back handling for cmd sending and the incoming pdu handling.
Normally, the cmd call back will be called before
receving the next PDU from the target if the application
uses the sync manner.
With the uring implementation, after sending the
cmd to the target, we may have the following scenerio:
(1) Firstly receive the incoming pdu(e.g., CapsuleResp pdu, C2hdata pdu)
due to the group polling read event.
(2) Secondly execute the callback function related with NVMe command sending.
This means that the data from the initiator is really sent out to the target,
and the target receives, then sending back the data to the initiator. But the
uring io_uring_cqe event is not handled, thus if we execute
(1) first, it will clean the data structures related with nvme_tcp_req, and the
nvme_tcp_req will be used for other purpose. Then causes wrong behaviour like
the following:
"Rand Write test failed at QD=128 because fio hangs with the following error:
nvme_tcp.c: 971:nvme_tcp_capsule_resp_hdr_handle: *ERROR*:
no tcp_req is found with cid=66 for tqpair=0x7f23d8001710".
And this patch can address this issue.
Signed-off-by: Ziye Yang <ziye.yang@intel.com>
Change-Id: I5043aaa8adf5033d93dedac15f633f0850e0b9f2
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/2818
Community-CI: Mellanox Build Bot
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: Aleksey Marchuk <alexeymar@mellanox.com>
2020-06-09 15:36:04 +00:00
|
|
|
|
2020-09-01 08:15:09 +00:00
|
|
|
/* Recv the pdu again */
|
|
|
|
nvme_tcp_qpair_set_recv_state(tqpair, NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_READY);
|
|
|
|
|
|
|
|
if (nvme_tcp_req_complete_safe(tcp_req)) {
|
|
|
|
(*reaped)++;
|
|
|
|
}
|
2018-08-02 02:21:45 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
end:
|
|
|
|
nvme_tcp_qpair_send_h2c_term_req(tqpair, pdu, fes, error_offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nvme_tcp_c2h_term_req_hdr_handle(struct nvme_tcp_qpair *tqpair,
|
|
|
|
struct nvme_tcp_pdu *pdu)
|
|
|
|
{
|
2019-09-27 21:19:57 +00:00
|
|
|
struct spdk_nvme_tcp_term_req_hdr *c2h_term_req = &pdu->hdr.term_req;
|
2018-08-02 02:21:45 +00:00
|
|
|
uint32_t error_offset = 0;
|
|
|
|
enum spdk_nvme_tcp_term_req_fes fes;
|
|
|
|
|
|
|
|
if (c2h_term_req->fes > SPDK_NVME_TCP_TERM_REQ_FES_INVALID_DATA_UNSUPPORTED_PARAMETER) {
|
2021-11-25 01:40:58 +00:00
|
|
|
SPDK_ERRLOG("Fatal Error Status(FES) is unknown for c2h_term_req pdu=%p\n", pdu);
|
2018-08-02 02:21:45 +00:00
|
|
|
fes = SPDK_NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
|
|
|
|
error_offset = offsetof(struct spdk_nvme_tcp_term_req_hdr, fes);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set the data buffer */
|
2019-09-27 21:19:57 +00:00
|
|
|
nvme_tcp_pdu_set_data(pdu, (uint8_t *)pdu->hdr.raw + c2h_term_req->common.hlen,
|
2019-04-03 09:29:33 +00:00
|
|
|
c2h_term_req->common.plen - c2h_term_req->common.hlen);
|
2018-08-02 02:21:45 +00:00
|
|
|
nvme_tcp_qpair_set_recv_state(tqpair, NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_PAYLOAD);
|
|
|
|
return;
|
|
|
|
end:
|
|
|
|
nvme_tcp_qpair_send_h2c_term_req(tqpair, pdu, fes, error_offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nvme_tcp_c2h_data_hdr_handle(struct nvme_tcp_qpair *tqpair, struct nvme_tcp_pdu *pdu)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_req *tcp_req;
|
2019-09-27 21:19:57 +00:00
|
|
|
struct spdk_nvme_tcp_c2h_data_hdr *c2h_data = &pdu->hdr.c2h_data;
|
2018-08-02 02:21:45 +00:00
|
|
|
uint32_t error_offset = 0;
|
|
|
|
enum spdk_nvme_tcp_term_req_fes fes;
|
2021-08-17 12:48:07 +00:00
|
|
|
int flags = c2h_data->common.flags;
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "enter\n");
|
|
|
|
SPDK_DEBUGLOG(nvme, "c2h_data info on tqpair(%p): datao=%u, datal=%u, cccid=%d\n",
|
2018-08-02 02:21:45 +00:00
|
|
|
tqpair, c2h_data->datao, c2h_data->datal, c2h_data->cccid);
|
|
|
|
tcp_req = get_nvme_active_req_by_cid(tqpair, c2h_data->cccid);
|
|
|
|
if (!tcp_req) {
|
|
|
|
SPDK_ERRLOG("no tcp_req found for c2hdata cid=%d\n", c2h_data->cccid);
|
|
|
|
fes = SPDK_NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
|
|
|
|
error_offset = offsetof(struct spdk_nvme_tcp_c2h_data_hdr, cccid);
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
nvme/tcp: Fix tcp_req->datao calculation issue.
When data digest is enabled for a nvme tcp qpair, we can use accel_fw
to calculate the data crc32c. Then if there are multiple
c2h pdus are coming, we can use both CPU resource directly
and accel_fw framework to caculate the checksum. Then the datao value compare
will not match since we will not update "datao" in the pdu coming order.
For example, if we receive 4 pdus, named as A, B, C, D.
offset data_len (in bytes)
A: 0 8192
B: 8192 4096
C: 12288 8192
D: 20480 4096
For receving the pdu, we hope that we can continue exeution even if
we use the offloading engine in accel_fw. Then in this situation,
if Pdu(C) is offloaded by accel_fw. Then our logic will continue receving
PDU(D). And according to the logic in our code, this time we leverage CPU
to calculate crc32c (Because we only have one active pdu to receive data).
Then we find the expected data offset is still 12288. Because "datao" in tcp_req will
only be updated after calling nvme_tcp_c2h_data_payload_handle function. So
while we enter nvme_tcp_c2h_data_hdr_handle function, we will find the
expected datao value is not as expected compared with the data offset value
contained in Pdu(D).
So the solution is that we create a new variable "expected_datao"
in tcp_req to do the comparation because we want to comply with the tp8000 spec
and do the offset check.
We still need use "datao" to count whether we receive the whole data or not.
So we cannot reuse "datao" variable in an early way. Otherwise, we will
release tcp_req structure early and cause another bug.
PS: This bug was not found early because previously the sw path in accel_fw
directly calculated the crc32c and called the user callback. Now we use a list and the
poller to handle, then it triggers this issue. Definitely, it will be much easier to
trigger this issue if we use real hardware engine.
Fixes #2098
Signed-off-by: Ziye Yang <ziye.yang@intel.com>
Change-Id: I10f5938a6342028d08d90820b2c14e4260134d77
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/9612
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Changpeng Liu <changpeng.liu@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: GangCao <gang.cao@intel.com>
Community-CI: Broadcom CI <spdk-ci.pdl@broadcom.com>
Community-CI: Mellanox Build Bot
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
2021-09-24 15:34:23 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "tcp_req(%p) on tqpair(%p): expected_datao=%u, payload_size=%u\n",
|
|
|
|
tcp_req, tqpair, tcp_req->expected_datao, tcp_req->req->payload_size);
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2021-08-17 12:48:07 +00:00
|
|
|
if (spdk_unlikely((flags & SPDK_NVME_TCP_C2H_DATA_FLAGS_SUCCESS) &&
|
|
|
|
!(flags & SPDK_NVME_TCP_C2H_DATA_FLAGS_LAST_PDU))) {
|
|
|
|
SPDK_ERRLOG("Invalid flag flags=%d in c2h_data=%p\n", flags, c2h_data);
|
|
|
|
fes = SPDK_NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
|
|
|
|
error_offset = offsetof(struct spdk_nvme_tcp_c2h_data_hdr, common);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2018-08-02 02:21:45 +00:00
|
|
|
if (c2h_data->datal > tcp_req->req->payload_size) {
|
|
|
|
SPDK_ERRLOG("Invalid datal for tcp_req(%p), datal(%u) exceeds payload_size(%u)\n",
|
|
|
|
tcp_req, c2h_data->datal, tcp_req->req->payload_size);
|
|
|
|
fes = SPDK_NVME_TCP_TERM_REQ_FES_DATA_TRANSFER_OUT_OF_RANGE;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
nvme/tcp: Fix tcp_req->datao calculation issue.
When data digest is enabled for a nvme tcp qpair, we can use accel_fw
to calculate the data crc32c. Then if there are multiple
c2h pdus are coming, we can use both CPU resource directly
and accel_fw framework to caculate the checksum. Then the datao value compare
will not match since we will not update "datao" in the pdu coming order.
For example, if we receive 4 pdus, named as A, B, C, D.
offset data_len (in bytes)
A: 0 8192
B: 8192 4096
C: 12288 8192
D: 20480 4096
For receving the pdu, we hope that we can continue exeution even if
we use the offloading engine in accel_fw. Then in this situation,
if Pdu(C) is offloaded by accel_fw. Then our logic will continue receving
PDU(D). And according to the logic in our code, this time we leverage CPU
to calculate crc32c (Because we only have one active pdu to receive data).
Then we find the expected data offset is still 12288. Because "datao" in tcp_req will
only be updated after calling nvme_tcp_c2h_data_payload_handle function. So
while we enter nvme_tcp_c2h_data_hdr_handle function, we will find the
expected datao value is not as expected compared with the data offset value
contained in Pdu(D).
So the solution is that we create a new variable "expected_datao"
in tcp_req to do the comparation because we want to comply with the tp8000 spec
and do the offset check.
We still need use "datao" to count whether we receive the whole data or not.
So we cannot reuse "datao" variable in an early way. Otherwise, we will
release tcp_req structure early and cause another bug.
PS: This bug was not found early because previously the sw path in accel_fw
directly calculated the crc32c and called the user callback. Now we use a list and the
poller to handle, then it triggers this issue. Definitely, it will be much easier to
trigger this issue if we use real hardware engine.
Fixes #2098
Signed-off-by: Ziye Yang <ziye.yang@intel.com>
Change-Id: I10f5938a6342028d08d90820b2c14e4260134d77
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/9612
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Changpeng Liu <changpeng.liu@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: GangCao <gang.cao@intel.com>
Community-CI: Broadcom CI <spdk-ci.pdl@broadcom.com>
Community-CI: Mellanox Build Bot
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
2021-09-24 15:34:23 +00:00
|
|
|
if (tcp_req->expected_datao != c2h_data->datao) {
|
|
|
|
SPDK_ERRLOG("Invalid datao for tcp_req(%p), received datal(%u) != expected datao(%u) in tcp_req\n",
|
|
|
|
tcp_req, c2h_data->datao, tcp_req->expected_datao);
|
2018-08-02 02:21:45 +00:00
|
|
|
fes = SPDK_NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
|
|
|
|
error_offset = offsetof(struct spdk_nvme_tcp_c2h_data_hdr, datao);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((c2h_data->datao + c2h_data->datal) > tcp_req->req->payload_size) {
|
|
|
|
SPDK_ERRLOG("Invalid data range for tcp_req(%p), received (datao(%u) + datal(%u)) > datao(%u) in tcp_req\n",
|
|
|
|
tcp_req, c2h_data->datao, c2h_data->datal, tcp_req->req->payload_size);
|
|
|
|
fes = SPDK_NVME_TCP_TERM_REQ_FES_DATA_TRANSFER_OUT_OF_RANGE;
|
|
|
|
error_offset = offsetof(struct spdk_nvme_tcp_c2h_data_hdr, datal);
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-05-24 01:51:32 +00:00
|
|
|
nvme_tcp_pdu_set_data_buf(pdu, tcp_req->iov, tcp_req->iovcnt,
|
|
|
|
c2h_data->datao, c2h_data->datal);
|
2019-10-01 20:45:35 +00:00
|
|
|
pdu->req = tcp_req;
|
2018-08-02 02:21:45 +00:00
|
|
|
|
|
|
|
nvme_tcp_qpair_set_recv_state(tqpair, NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_PAYLOAD);
|
|
|
|
return;
|
|
|
|
|
|
|
|
end:
|
|
|
|
nvme_tcp_qpair_send_h2c_term_req(tqpair, pdu, fes, error_offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nvme_tcp_qpair_h2c_data_send_complete(void *cb_arg)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_req *tcp_req = cb_arg;
|
|
|
|
|
|
|
|
assert(tcp_req != NULL);
|
|
|
|
|
2020-09-01 07:03:16 +00:00
|
|
|
tcp_req->ordering.bits.send_ack = 1;
|
2018-08-02 02:21:45 +00:00
|
|
|
if (tcp_req->r2tl_remain) {
|
2020-05-10 23:32:35 +00:00
|
|
|
nvme_tcp_send_h2c_data(tcp_req);
|
2019-05-27 10:06:28 +00:00
|
|
|
} else {
|
|
|
|
assert(tcp_req->active_r2ts > 0);
|
|
|
|
tcp_req->active_r2ts--;
|
|
|
|
tcp_req->state = NVME_TCP_REQ_ACTIVE;
|
2020-09-01 07:43:51 +00:00
|
|
|
|
|
|
|
if (tcp_req->ordering.bits.r2t_waiting_h2c_complete) {
|
|
|
|
tcp_req->ordering.bits.r2t_waiting_h2c_complete = 0;
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "tcp_req %p: continue r2t\n", tcp_req);
|
2020-09-01 07:43:51 +00:00
|
|
|
assert(tcp_req->active_r2ts > 0);
|
|
|
|
tcp_req->ttag = tcp_req->ttag_r2t_next;
|
|
|
|
tcp_req->r2tl_remain = tcp_req->r2tl_remain_next;
|
|
|
|
tcp_req->state = NVME_TCP_REQ_ACTIVE_R2T;
|
|
|
|
nvme_tcp_send_h2c_data(tcp_req);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-06-25 09:56:32 +00:00
|
|
|
/* Need also call this function to free the resource */
|
2020-09-01 08:15:09 +00:00
|
|
|
nvme_tcp_req_complete_safe(tcp_req);
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-05-10 23:32:35 +00:00
|
|
|
nvme_tcp_send_h2c_data(struct nvme_tcp_req *tcp_req)
|
2018-08-02 02:21:45 +00:00
|
|
|
{
|
|
|
|
struct nvme_tcp_qpair *tqpair = nvme_tcp_qpair(tcp_req->req->qpair);
|
|
|
|
struct nvme_tcp_pdu *rsp_pdu;
|
|
|
|
struct spdk_nvme_tcp_h2c_data_hdr *h2c_data;
|
|
|
|
uint32_t plen, pdo, alignment;
|
|
|
|
|
2020-09-01 07:18:33 +00:00
|
|
|
/* Reinit the send_ack and h2c_send_waiting_ack bits */
|
2020-09-01 07:03:16 +00:00
|
|
|
tcp_req->ordering.bits.send_ack = 0;
|
2020-09-01 07:18:33 +00:00
|
|
|
tcp_req->ordering.bits.h2c_send_waiting_ack = 0;
|
2021-05-25 15:12:20 +00:00
|
|
|
rsp_pdu = tcp_req->pdu;
|
2018-08-02 02:21:45 +00:00
|
|
|
memset(rsp_pdu, 0, sizeof(*rsp_pdu));
|
2019-09-27 21:19:57 +00:00
|
|
|
h2c_data = &rsp_pdu->hdr.h2c_data;
|
2018-08-02 02:21:45 +00:00
|
|
|
|
|
|
|
h2c_data->common.pdu_type = SPDK_NVME_TCP_PDU_TYPE_H2C_DATA;
|
|
|
|
plen = h2c_data->common.hlen = sizeof(*h2c_data);
|
|
|
|
h2c_data->cccid = tcp_req->cid;
|
|
|
|
h2c_data->ttag = tcp_req->ttag;
|
|
|
|
h2c_data->datao = tcp_req->datao;
|
|
|
|
|
|
|
|
h2c_data->datal = spdk_min(tcp_req->r2tl_remain, tqpair->maxh2cdata);
|
2019-05-24 01:51:32 +00:00
|
|
|
nvme_tcp_pdu_set_data_buf(rsp_pdu, tcp_req->iov, tcp_req->iovcnt,
|
|
|
|
h2c_data->datao, h2c_data->datal);
|
2018-08-02 02:21:45 +00:00
|
|
|
tcp_req->r2tl_remain -= h2c_data->datal;
|
|
|
|
|
2020-09-01 08:23:25 +00:00
|
|
|
if (tqpair->flags.host_hdgst_enable) {
|
2018-08-02 02:21:45 +00:00
|
|
|
h2c_data->common.flags |= SPDK_NVME_TCP_CH_FLAGS_HDGSTF;
|
|
|
|
plen += SPDK_NVME_TCP_DIGEST_LEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
rsp_pdu->padding_len = 0;
|
|
|
|
pdo = plen;
|
|
|
|
if (tqpair->cpda) {
|
|
|
|
alignment = (tqpair->cpda + 1) << 2;
|
|
|
|
if (alignment > plen) {
|
|
|
|
rsp_pdu->padding_len = alignment - plen;
|
|
|
|
pdo = plen = alignment;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
h2c_data->common.pdo = pdo;
|
|
|
|
plen += h2c_data->datal;
|
2020-09-01 08:23:25 +00:00
|
|
|
if (tqpair->flags.host_ddgst_enable) {
|
2018-08-02 02:21:45 +00:00
|
|
|
h2c_data->common.flags |= SPDK_NVME_TCP_CH_FLAGS_DDGSTF;
|
|
|
|
plen += SPDK_NVME_TCP_DIGEST_LEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
h2c_data->common.plen = plen;
|
|
|
|
tcp_req->datao += h2c_data->datal;
|
|
|
|
if (!tcp_req->r2tl_remain) {
|
|
|
|
h2c_data->common.flags |= SPDK_NVME_TCP_H2C_DATA_FLAGS_LAST_PDU;
|
|
|
|
}
|
|
|
|
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "h2c_data info: datao=%u, datal=%u, pdu_len=%u for tqpair=%p\n",
|
2018-08-02 02:21:45 +00:00
|
|
|
h2c_data->datao, h2c_data->datal, h2c_data->common.plen, tqpair);
|
|
|
|
|
|
|
|
nvme_tcp_qpair_write_pdu(tqpair, rsp_pdu, nvme_tcp_qpair_h2c_data_send_complete, tcp_req);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nvme_tcp_r2t_hdr_handle(struct nvme_tcp_qpair *tqpair, struct nvme_tcp_pdu *pdu)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_req *tcp_req;
|
2019-09-27 21:19:57 +00:00
|
|
|
struct spdk_nvme_tcp_r2t_hdr *r2t = &pdu->hdr.r2t;
|
2018-08-02 02:21:45 +00:00
|
|
|
uint32_t cid, error_offset = 0;
|
|
|
|
enum spdk_nvme_tcp_term_req_fes fes;
|
|
|
|
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "enter\n");
|
2018-08-02 02:21:45 +00:00
|
|
|
cid = r2t->cccid;
|
|
|
|
tcp_req = get_nvme_active_req_by_cid(tqpair, cid);
|
|
|
|
if (!tcp_req) {
|
|
|
|
SPDK_ERRLOG("Cannot find tcp_req for tqpair=%p\n", tqpair);
|
|
|
|
fes = SPDK_NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
|
|
|
|
error_offset = offsetof(struct spdk_nvme_tcp_r2t_hdr, cccid);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "r2t info: r2to=%u, r2tl=%u for tqpair=%p\n", r2t->r2to, r2t->r2tl,
|
2018-08-02 02:21:45 +00:00
|
|
|
tqpair);
|
|
|
|
|
2019-05-27 10:06:28 +00:00
|
|
|
if (tcp_req->state == NVME_TCP_REQ_ACTIVE) {
|
|
|
|
assert(tcp_req->active_r2ts == 0);
|
2018-08-02 02:21:45 +00:00
|
|
|
tcp_req->state = NVME_TCP_REQ_ACTIVE_R2T;
|
2019-05-27 10:06:28 +00:00
|
|
|
}
|
|
|
|
|
2018-08-02 02:21:45 +00:00
|
|
|
if (tcp_req->datao != r2t->r2to) {
|
|
|
|
fes = SPDK_NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
|
|
|
|
error_offset = offsetof(struct spdk_nvme_tcp_r2t_hdr, r2to);
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((r2t->r2tl + r2t->r2to) > tcp_req->req->payload_size) {
|
|
|
|
SPDK_ERRLOG("Invalid R2T info for tcp_req=%p: (r2to(%u) + r2tl(%u)) exceeds payload_size(%u)\n",
|
|
|
|
tcp_req, r2t->r2to, r2t->r2tl, tqpair->maxh2cdata);
|
|
|
|
fes = SPDK_NVME_TCP_TERM_REQ_FES_DATA_TRANSFER_OUT_OF_RANGE;
|
|
|
|
error_offset = offsetof(struct spdk_nvme_tcp_r2t_hdr, r2tl);
|
|
|
|
goto end;
|
2020-09-01 07:43:51 +00:00
|
|
|
}
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2020-09-01 07:43:51 +00:00
|
|
|
tcp_req->active_r2ts++;
|
|
|
|
if (spdk_unlikely(tcp_req->active_r2ts > tqpair->maxr2t)) {
|
|
|
|
if (tcp_req->state == NVME_TCP_REQ_ACTIVE_R2T && !tcp_req->ordering.bits.send_ack) {
|
|
|
|
/* We receive a subsequent R2T while we are waiting for H2C transfer to complete */
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "received a subsequent R2T\n");
|
2020-09-01 07:43:51 +00:00
|
|
|
assert(tcp_req->active_r2ts == tqpair->maxr2t + 1);
|
|
|
|
tcp_req->ttag_r2t_next = r2t->ttag;
|
|
|
|
tcp_req->r2tl_remain_next = r2t->r2tl;
|
|
|
|
tcp_req->ordering.bits.r2t_waiting_h2c_complete = 1;
|
|
|
|
nvme_tcp_qpair_set_recv_state(tqpair, NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_READY);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
fes = SPDK_NVME_TCP_TERM_REQ_FES_R2T_LIMIT_EXCEEDED;
|
|
|
|
SPDK_ERRLOG("Invalid R2T: Maximum number of R2T exceeded! Max: %u for tqpair=%p\n", tqpair->maxr2t,
|
|
|
|
tqpair);
|
|
|
|
goto end;
|
|
|
|
}
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
tcp_req->ttag = r2t->ttag;
|
|
|
|
tcp_req->r2tl_remain = r2t->r2tl;
|
|
|
|
nvme_tcp_qpair_set_recv_state(tqpair, NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_READY);
|
|
|
|
|
2020-09-01 07:03:16 +00:00
|
|
|
if (spdk_likely(tcp_req->ordering.bits.send_ack)) {
|
2020-07-04 16:23:25 +00:00
|
|
|
nvme_tcp_send_h2c_data(tcp_req);
|
2020-09-01 07:18:33 +00:00
|
|
|
} else {
|
|
|
|
tcp_req->ordering.bits.h2c_send_waiting_ack = 1;
|
2020-07-04 16:23:25 +00:00
|
|
|
}
|
2020-09-01 07:18:33 +00:00
|
|
|
|
2018-08-02 02:21:45 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
end:
|
|
|
|
nvme_tcp_qpair_send_h2c_term_req(tqpair, pdu, fes, error_offset);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nvme_tcp_pdu_psh_handle(struct nvme_tcp_qpair *tqpair, uint32_t *reaped)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_pdu *pdu;
|
|
|
|
int rc;
|
|
|
|
uint32_t crc32c, error_offset = 0;
|
|
|
|
enum spdk_nvme_tcp_term_req_fes fes;
|
|
|
|
|
|
|
|
assert(tqpair->recv_state == NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_PSH);
|
2021-05-06 14:13:11 +00:00
|
|
|
pdu = tqpair->recv_pdu;
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "enter: pdu type =%u\n", pdu->hdr.common.pdu_type);
|
2018-08-02 02:21:45 +00:00
|
|
|
/* check header digest if needed */
|
|
|
|
if (pdu->has_hdgst) {
|
|
|
|
crc32c = nvme_tcp_pdu_calc_header_digest(pdu);
|
2019-09-27 21:19:57 +00:00
|
|
|
rc = MATCH_DIGEST_WORD((uint8_t *)pdu->hdr.raw + pdu->hdr.common.hlen, crc32c);
|
2018-08-02 02:21:45 +00:00
|
|
|
if (rc == 0) {
|
|
|
|
SPDK_ERRLOG("header digest error on tqpair=(%p) with pdu=%p\n", tqpair, pdu);
|
|
|
|
fes = SPDK_NVME_TCP_TERM_REQ_FES_HDGST_ERROR;
|
|
|
|
nvme_tcp_qpair_send_h2c_term_req(tqpair, pdu, fes, error_offset);
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-27 21:19:57 +00:00
|
|
|
switch (pdu->hdr.common.pdu_type) {
|
2018-08-02 02:21:45 +00:00
|
|
|
case SPDK_NVME_TCP_PDU_TYPE_IC_RESP:
|
|
|
|
nvme_tcp_icresp_handle(tqpair, pdu);
|
|
|
|
break;
|
|
|
|
case SPDK_NVME_TCP_PDU_TYPE_CAPSULE_RESP:
|
|
|
|
nvme_tcp_capsule_resp_hdr_handle(tqpair, pdu, reaped);
|
|
|
|
break;
|
|
|
|
case SPDK_NVME_TCP_PDU_TYPE_C2H_DATA:
|
|
|
|
nvme_tcp_c2h_data_hdr_handle(tqpair, pdu);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SPDK_NVME_TCP_PDU_TYPE_C2H_TERM_REQ:
|
|
|
|
nvme_tcp_c2h_term_req_hdr_handle(tqpair, pdu);
|
|
|
|
break;
|
|
|
|
case SPDK_NVME_TCP_PDU_TYPE_R2T:
|
|
|
|
nvme_tcp_r2t_hdr_handle(tqpair, pdu);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2021-05-06 14:13:11 +00:00
|
|
|
SPDK_ERRLOG("Unexpected PDU type 0x%02x\n", tqpair->recv_pdu->hdr.common.pdu_type);
|
2018-08-02 02:21:45 +00:00
|
|
|
fes = SPDK_NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
|
|
|
|
error_offset = 1;
|
|
|
|
nvme_tcp_qpair_send_h2c_term_req(tqpair, pdu, fes, error_offset);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
nvme_tcp_read_pdu(struct nvme_tcp_qpair *tqpair, uint32_t *reaped)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
struct nvme_tcp_pdu *pdu;
|
|
|
|
uint32_t data_len;
|
|
|
|
enum nvme_tcp_pdu_recv_state prev_state;
|
|
|
|
|
|
|
|
/* The loop here is to allow for several back-to-back state changes. */
|
|
|
|
do {
|
|
|
|
prev_state = tqpair->recv_state;
|
|
|
|
switch (tqpair->recv_state) {
|
|
|
|
/* If in a new state */
|
|
|
|
case NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_READY:
|
|
|
|
nvme_tcp_qpair_set_recv_state(tqpair, NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_CH);
|
|
|
|
break;
|
|
|
|
/* common header */
|
|
|
|
case NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_CH:
|
2021-05-06 14:13:11 +00:00
|
|
|
pdu = tqpair->recv_pdu;
|
2018-08-02 02:21:45 +00:00
|
|
|
if (pdu->ch_valid_bytes < sizeof(struct spdk_nvme_tcp_common_pdu_hdr)) {
|
|
|
|
rc = nvme_tcp_read_data(tqpair->sock,
|
|
|
|
sizeof(struct spdk_nvme_tcp_common_pdu_hdr) - pdu->ch_valid_bytes,
|
2019-09-27 21:19:57 +00:00
|
|
|
(uint8_t *)&pdu->hdr.common + pdu->ch_valid_bytes);
|
2018-08-02 02:21:45 +00:00
|
|
|
if (rc < 0) {
|
|
|
|
nvme_tcp_qpair_set_recv_state(tqpair, NVME_TCP_PDU_RECV_STATE_ERROR);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pdu->ch_valid_bytes += rc;
|
|
|
|
if (pdu->ch_valid_bytes < sizeof(struct spdk_nvme_tcp_common_pdu_hdr)) {
|
2020-09-01 08:53:46 +00:00
|
|
|
rc = NVME_TCP_PDU_IN_PROGRESS;
|
|
|
|
goto out;
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The command header of this PDU has now been read from the socket. */
|
|
|
|
nvme_tcp_pdu_ch_handle(tqpair);
|
|
|
|
break;
|
|
|
|
/* Wait for the pdu specific header */
|
|
|
|
case NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_PSH:
|
2021-05-06 14:13:11 +00:00
|
|
|
pdu = tqpair->recv_pdu;
|
2019-08-06 09:34:45 +00:00
|
|
|
rc = nvme_tcp_read_data(tqpair->sock,
|
|
|
|
pdu->psh_len - pdu->psh_valid_bytes,
|
2019-09-27 21:19:57 +00:00
|
|
|
(uint8_t *)&pdu->hdr.raw + sizeof(struct spdk_nvme_tcp_common_pdu_hdr) + pdu->psh_valid_bytes);
|
2019-08-06 09:34:45 +00:00
|
|
|
if (rc < 0) {
|
|
|
|
nvme_tcp_qpair_set_recv_state(tqpair, NVME_TCP_PDU_RECV_STATE_ERROR);
|
|
|
|
break;
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
2019-08-06 09:34:45 +00:00
|
|
|
pdu->psh_valid_bytes += rc;
|
|
|
|
if (pdu->psh_valid_bytes < pdu->psh_len) {
|
2020-09-01 08:53:46 +00:00
|
|
|
rc = NVME_TCP_PDU_IN_PROGRESS;
|
|
|
|
goto out;
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* All header(ch, psh, head digist) of this PDU has now been read from the socket. */
|
|
|
|
nvme_tcp_pdu_psh_handle(tqpair, reaped);
|
|
|
|
break;
|
|
|
|
case NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_PAYLOAD:
|
2021-05-06 14:13:11 +00:00
|
|
|
pdu = tqpair->recv_pdu;
|
2018-08-02 02:21:45 +00:00
|
|
|
/* check whether the data is valid, if not we just return */
|
2019-03-18 11:28:12 +00:00
|
|
|
if (!pdu->data_len) {
|
2018-08-02 02:21:45 +00:00
|
|
|
return NVME_TCP_PDU_IN_PROGRESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
data_len = pdu->data_len;
|
2019-03-11 18:21:56 +00:00
|
|
|
/* data digest */
|
2019-09-27 21:19:57 +00:00
|
|
|
if (spdk_unlikely((pdu->hdr.common.pdu_type == SPDK_NVME_TCP_PDU_TYPE_C2H_DATA) &&
|
2020-09-01 08:23:25 +00:00
|
|
|
tqpair->flags.host_ddgst_enable)) {
|
2019-03-11 18:21:56 +00:00
|
|
|
data_len += SPDK_NVME_TCP_DIGEST_LEN;
|
|
|
|
pdu->ddgst_enable = true;
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
2019-03-11 18:21:56 +00:00
|
|
|
rc = nvme_tcp_read_payload_data(tqpair->sock, pdu);
|
|
|
|
if (rc < 0) {
|
|
|
|
nvme_tcp_qpair_set_recv_state(tqpair, NVME_TCP_PDU_RECV_STATE_ERROR);
|
|
|
|
break;
|
|
|
|
}
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2021-01-14 16:54:17 +00:00
|
|
|
pdu->rw_offset += rc;
|
|
|
|
if (pdu->rw_offset < data_len) {
|
2020-09-01 08:53:46 +00:00
|
|
|
rc = NVME_TCP_PDU_IN_PROGRESS;
|
|
|
|
goto out;
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
2021-01-14 16:54:17 +00:00
|
|
|
assert(pdu->rw_offset == data_len);
|
2018-08-02 02:21:45 +00:00
|
|
|
/* All of this PDU has now been read from the socket. */
|
|
|
|
nvme_tcp_pdu_payload_handle(tqpair, reaped);
|
|
|
|
break;
|
|
|
|
case NVME_TCP_PDU_RECV_STATE_ERROR:
|
|
|
|
rc = NVME_TCP_PDU_FATAL;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (prev_state != tqpair->recv_state);
|
|
|
|
|
2020-09-01 08:53:46 +00:00
|
|
|
out:
|
|
|
|
*reaped += tqpair->async_complete;
|
|
|
|
tqpair->async_complete = 0;
|
|
|
|
|
2018-08-02 02:21:45 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2019-02-15 11:22:54 +00:00
|
|
|
static void
|
|
|
|
nvme_tcp_qpair_check_timeout(struct spdk_nvme_qpair *qpair)
|
|
|
|
{
|
|
|
|
uint64_t t02;
|
|
|
|
struct nvme_tcp_req *tcp_req, *tmp;
|
|
|
|
struct nvme_tcp_qpair *tqpair = nvme_tcp_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)) {
|
2020-05-15 03:50:18 +00:00
|
|
|
active_proc = nvme_ctrlr_get_current_process(ctrlr);
|
2019-02-15 11:22:54 +00:00
|
|
|
} 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(tcp_req, &tqpair->outstanding_reqs, link, tmp) {
|
|
|
|
assert(tcp_req->req != NULL);
|
|
|
|
|
|
|
|
if (nvme_request_check_timeout(tcp_req->req, tcp_req->cid, active_proc, t02)) {
|
|
|
|
/*
|
|
|
|
* The requests are in order, so as soon as one has not timed out,
|
|
|
|
* stop iterating.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-03 09:55:26 +00:00
|
|
|
static int nvme_tcp_ctrlr_connect_qpair_poll(struct spdk_nvme_ctrlr *ctrlr,
|
|
|
|
struct spdk_nvme_qpair *qpair);
|
|
|
|
|
2020-02-10 19:13:06 +00:00
|
|
|
static int
|
2018-08-02 02:21:45 +00:00
|
|
|
nvme_tcp_qpair_process_completions(struct spdk_nvme_qpair *qpair, uint32_t max_completions)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_qpair *tqpair = nvme_tcp_qpair(qpair);
|
|
|
|
uint32_t reaped;
|
|
|
|
int rc;
|
|
|
|
|
2021-03-24 20:17:24 +00:00
|
|
|
if (qpair->poll_group == NULL) {
|
|
|
|
rc = spdk_sock_flush(tqpair->sock);
|
|
|
|
if (rc < 0) {
|
|
|
|
return rc;
|
|
|
|
}
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (max_completions == 0) {
|
|
|
|
max_completions = tqpair->num_entries;
|
|
|
|
} else {
|
|
|
|
max_completions = spdk_min(max_completions, tqpair->num_entries);
|
|
|
|
}
|
|
|
|
|
|
|
|
reaped = 0;
|
|
|
|
do {
|
|
|
|
rc = nvme_tcp_read_pdu(tqpair, &reaped);
|
|
|
|
if (rc < 0) {
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "Error polling CQ! (%d): %s\n",
|
2019-07-05 19:24:17 +00:00
|
|
|
errno, spdk_strerror(errno));
|
2019-11-13 15:43:16 +00:00
|
|
|
goto fail;
|
2018-08-02 02:21:45 +00:00
|
|
|
} else if (rc == 0) {
|
|
|
|
/* Partial PDU is read */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (reaped < max_completions);
|
|
|
|
|
2019-02-15 11:22:54 +00:00
|
|
|
if (spdk_unlikely(tqpair->qpair.ctrlr->timeout_enabled)) {
|
|
|
|
nvme_tcp_qpair_check_timeout(qpair);
|
|
|
|
}
|
|
|
|
|
2021-08-03 09:55:26 +00:00
|
|
|
if (spdk_unlikely(nvme_qpair_get_state(qpair) == NVME_QPAIR_CONNECTING)) {
|
|
|
|
rc = nvme_tcp_ctrlr_connect_qpair_poll(qpair->ctrlr, qpair);
|
|
|
|
if (rc != 0 && rc != -EAGAIN) {
|
|
|
|
SPDK_ERRLOG("Failed to connect tqpair=%p\n", tqpair);
|
|
|
|
goto fail;
|
|
|
|
} else if (rc == 0) {
|
|
|
|
/* Once the connection is completed, we can submit queued requests */
|
|
|
|
nvme_qpair_resubmit_requests(qpair, tqpair->num_entries);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-02 02:21:45 +00:00
|
|
|
return reaped;
|
2019-11-13 15:43:16 +00:00
|
|
|
fail:
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Since admin queues take the ctrlr_lock before entering this function,
|
2020-04-09 20:28:41 +00:00
|
|
|
* we can call nvme_transport_ctrlr_disconnect_qpair. For other qpairs we need
|
2019-11-13 15:43:16 +00:00
|
|
|
* to call the generic function which will take the lock for us.
|
|
|
|
*/
|
2019-11-25 23:17:50 +00:00
|
|
|
qpair->transport_failure_reason = SPDK_NVME_QPAIR_FAILURE_UNKNOWN;
|
|
|
|
|
2019-11-13 15:43:16 +00:00
|
|
|
if (nvme_qpair_is_admin_queue(qpair)) {
|
2020-04-14 21:41:11 +00:00
|
|
|
nvme_transport_ctrlr_disconnect_qpair(qpair->ctrlr, qpair);
|
2019-11-13 15:43:16 +00:00
|
|
|
} else {
|
|
|
|
nvme_ctrlr_disconnect_qpair(qpair);
|
|
|
|
}
|
|
|
|
return -ENXIO;
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
2020-02-13 21:52:59 +00:00
|
|
|
static void
|
|
|
|
nvme_tcp_qpair_sock_cb(void *ctx, struct spdk_sock_group *group, struct spdk_sock *sock)
|
|
|
|
{
|
|
|
|
struct spdk_nvme_qpair *qpair = ctx;
|
|
|
|
struct nvme_tcp_poll_group *pgroup = nvme_tcp_poll_group(qpair->poll_group);
|
|
|
|
int32_t num_completions;
|
2021-03-05 21:00:12 +00:00
|
|
|
struct nvme_tcp_qpair *tqpair = nvme_tcp_qpair(qpair);
|
|
|
|
|
|
|
|
if (tqpair->needs_poll) {
|
|
|
|
TAILQ_REMOVE(&pgroup->needs_poll, tqpair, link);
|
|
|
|
tqpair->needs_poll = false;
|
|
|
|
}
|
2020-02-13 21:52:59 +00:00
|
|
|
|
|
|
|
num_completions = spdk_nvme_qpair_process_completions(qpair, pgroup->completions_per_qpair);
|
|
|
|
|
|
|
|
if (pgroup->num_completions >= 0 && num_completions >= 0) {
|
|
|
|
pgroup->num_completions += num_completions;
|
2021-07-19 08:57:48 +00:00
|
|
|
pgroup->stats.nvme_completions += num_completions;
|
2020-02-13 21:52:59 +00:00
|
|
|
} else {
|
|
|
|
pgroup->num_completions = -ENXIO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-02 02:21:45 +00:00
|
|
|
static int
|
|
|
|
nvme_tcp_qpair_icreq_send(struct nvme_tcp_qpair *tqpair)
|
|
|
|
{
|
|
|
|
struct spdk_nvme_tcp_ic_req *ic_req;
|
|
|
|
struct nvme_tcp_pdu *pdu;
|
|
|
|
|
2020-07-30 11:43:51 +00:00
|
|
|
pdu = tqpair->send_pdu;
|
|
|
|
memset(tqpair->send_pdu, 0, sizeof(*tqpair->send_pdu));
|
2019-09-27 21:19:57 +00:00
|
|
|
ic_req = &pdu->hdr.ic_req;
|
2018-08-02 02:21:45 +00:00
|
|
|
|
|
|
|
ic_req->common.pdu_type = SPDK_NVME_TCP_PDU_TYPE_IC_REQ;
|
|
|
|
ic_req->common.hlen = ic_req->common.plen = sizeof(*ic_req);
|
|
|
|
ic_req->pfv = 0;
|
|
|
|
ic_req->maxr2t = NVME_TCP_MAX_R2T_DEFAULT - 1;
|
|
|
|
ic_req->hpda = NVME_TCP_HPDA_DEFAULT;
|
|
|
|
|
2018-11-30 01:46:55 +00:00
|
|
|
ic_req->dgst.bits.hdgst_enable = tqpair->qpair.ctrlr->opts.header_digest;
|
|
|
|
ic_req->dgst.bits.ddgst_enable = tqpair->qpair.ctrlr->opts.data_digest;
|
2018-08-02 02:21:45 +00:00
|
|
|
|
|
|
|
nvme_tcp_qpair_write_pdu(tqpair, pdu, nvme_tcp_send_icreq_complete, tqpair);
|
|
|
|
|
2021-05-28 16:47:16 +00:00
|
|
|
tqpair->icreq_timeout_tsc = spdk_get_ticks() + (NVME_TCP_TIME_OUT_IN_SECONDS * spdk_get_ticks_hz());
|
2021-05-28 16:42:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2020-02-10 19:13:06 +00:00
|
|
|
static int
|
2020-12-14 15:31:30 +00:00
|
|
|
nvme_tcp_qpair_connect_sock(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair)
|
2018-08-02 02:21:45 +00:00
|
|
|
{
|
|
|
|
struct sockaddr_storage dst_addr;
|
|
|
|
struct sockaddr_storage src_addr;
|
|
|
|
int rc;
|
2019-12-27 00:20:07 +00:00
|
|
|
struct nvme_tcp_qpair *tqpair;
|
2018-08-02 02:21:45 +00:00
|
|
|
int family;
|
2019-01-24 23:15:42 +00:00
|
|
|
long int port;
|
2020-02-19 11:18:51 +00:00
|
|
|
struct spdk_sock_opts opts;
|
2018-08-02 02:21:45 +00:00
|
|
|
|
2019-12-27 00:20:07 +00:00
|
|
|
tqpair = nvme_tcp_qpair(qpair);
|
2018-08-02 02:21:45 +00:00
|
|
|
|
|
|
|
switch (ctrlr->trid.adrfam) {
|
|
|
|
case SPDK_NVMF_ADRFAM_IPV4:
|
|
|
|
family = AF_INET;
|
|
|
|
break;
|
|
|
|
case SPDK_NVMF_ADRFAM_IPV6:
|
|
|
|
family = AF_INET6;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
SPDK_ERRLOG("Unhandled ADRFAM %d\n", ctrlr->trid.adrfam);
|
2020-08-17 12:46:05 +00:00
|
|
|
rc = -1;
|
|
|
|
return rc;
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "adrfam %d ai_family %d\n", ctrlr->trid.adrfam, family);
|
2018-08-02 02:21:45 +00:00
|
|
|
|
|
|
|
memset(&dst_addr, 0, sizeof(dst_addr));
|
|
|
|
|
2020-09-04 11:27:29 +00:00
|
|
|
SPDK_DEBUGLOG(nvme, "trsvcid is %s\n", ctrlr->trid.trsvcid);
|
2018-08-02 02:21:45 +00:00
|
|
|
rc = nvme_tcp_parse_addr(&dst_addr, family, ctrlr->trid.traddr, ctrlr->trid.trsvcid);
|
|
|
|
if (rc != 0) {
|
|
|
|
SPDK_ERRLOG("dst_addr nvme_tcp_parse_addr() failed\n");
|
2020-08-17 12:46:05 +00:00
|
|
|
return rc;
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ctrlr->opts.src_addr[0] || ctrlr->opts.src_svcid[0]) {
|
|
|
|
memset(&src_addr, 0, sizeof(src_addr));
|
|
|
|
rc = nvme_tcp_parse_addr(&src_addr, family, ctrlr->opts.src_addr, ctrlr->opts.src_svcid);
|
|
|
|
if (rc != 0) {
|
|
|
|
SPDK_ERRLOG("src_addr nvme_tcp_parse_addr() failed\n");
|
2020-08-17 12:46:05 +00:00
|
|
|
return rc;
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-24 23:15:42 +00:00
|
|
|
port = spdk_strtol(ctrlr->trid.trsvcid, 10);
|
|
|
|
if (port <= 0 || port >= INT_MAX) {
|
|
|
|
SPDK_ERRLOG("Invalid port: %s\n", ctrlr->trid.trsvcid);
|
2020-08-17 12:46:05 +00:00
|
|
|
rc = -1;
|
|
|
|
return rc;
|
2019-01-24 23:15:42 +00:00
|
|
|
}
|
|
|
|
|
2020-02-19 11:18:51 +00:00
|
|
|
opts.opts_size = sizeof(opts);
|
|
|
|
spdk_sock_get_default_opts(&opts);
|
|
|
|
opts.priority = ctrlr->trid.priority;
|
2021-03-25 17:35:53 +00:00
|
|
|
opts.zcopy = !nvme_qpair_is_admin_queue(qpair);
|
2020-02-19 11:18:51 +00:00
|
|
|
tqpair->sock = spdk_sock_connect_ext(ctrlr->trid.traddr, port, NULL, &opts);
|
2018-08-02 02:21:45 +00:00
|
|
|
if (!tqpair->sock) {
|
2019-01-24 23:15:42 +00:00
|
|
|
SPDK_ERRLOG("sock connection error of tqpair=%p with addr=%s, port=%ld\n",
|
|
|
|
tqpair, ctrlr->trid.traddr, port);
|
2020-08-17 12:46:05 +00:00
|
|
|
rc = -1;
|
|
|
|
return rc;
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
2020-12-14 15:31:30 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-05-28 16:42:48 +00:00
|
|
|
static int
|
|
|
|
nvme_tcp_ctrlr_connect_qpair_poll(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_qpair *tqpair;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
tqpair = nvme_tcp_qpair(qpair);
|
|
|
|
|
2021-07-02 08:17:52 +00:00
|
|
|
/* Prevent this function from being called recursively, as it could lead to issues with
|
|
|
|
* nvme_fabric_qpair_connect_poll() if the connect response is received in the recursive
|
|
|
|
* call.
|
|
|
|
*/
|
|
|
|
if (tqpair->flags.in_connect_poll) {
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
tqpair->flags.in_connect_poll = 1;
|
|
|
|
|
2021-05-28 16:42:48 +00:00
|
|
|
switch (tqpair->state) {
|
|
|
|
case NVME_TCP_QPAIR_STATE_INVALID:
|
|
|
|
case NVME_TCP_QPAIR_STATE_INITIALIZING:
|
2021-08-03 09:55:26 +00:00
|
|
|
if (spdk_get_ticks() > tqpair->icreq_timeout_tsc) {
|
2021-05-28 16:42:48 +00:00
|
|
|
SPDK_ERRLOG("Failed to construct the tqpair=%p via correct icresp\n", tqpair);
|
2021-08-03 09:55:26 +00:00
|
|
|
rc = -ETIMEDOUT;
|
|
|
|
break;
|
2021-05-28 16:42:48 +00:00
|
|
|
}
|
2021-08-03 09:55:26 +00:00
|
|
|
rc = -EAGAIN;
|
2021-05-28 16:42:48 +00:00
|
|
|
break;
|
2021-06-08 19:33:58 +00:00
|
|
|
case NVME_TCP_QPAIR_STATE_FABRIC_CONNECT_SEND:
|
|
|
|
rc = nvme_fabric_qpair_connect_async(&tqpair->qpair, tqpair->num_entries);
|
2021-05-28 16:42:48 +00:00
|
|
|
if (rc < 0) {
|
|
|
|
SPDK_ERRLOG("Failed to send an NVMe-oF Fabric CONNECT command\n");
|
|
|
|
break;
|
|
|
|
}
|
2021-06-08 19:33:58 +00:00
|
|
|
tqpair->state = NVME_TCP_QPAIR_STATE_FABRIC_CONNECT_POLL;
|
|
|
|
rc = -EAGAIN;
|
|
|
|
break;
|
|
|
|
case NVME_TCP_QPAIR_STATE_FABRIC_CONNECT_POLL:
|
|
|
|
rc = nvme_fabric_qpair_connect_poll(&tqpair->qpair);
|
|
|
|
if (rc == 0) {
|
|
|
|
tqpair->state = NVME_TCP_QPAIR_STATE_RUNNING;
|
|
|
|
nvme_qpair_set_state(qpair, NVME_QPAIR_CONNECTED);
|
|
|
|
} else if (rc != -EAGAIN) {
|
|
|
|
SPDK_ERRLOG("Failed to poll NVMe-oF Fabric CONNECT command\n");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case NVME_TCP_QPAIR_STATE_RUNNING:
|
|
|
|
rc = 0;
|
2021-05-28 16:42:48 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
rc = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-07-02 08:17:52 +00:00
|
|
|
tqpair->flags.in_connect_poll = 0;
|
2021-05-28 16:42:48 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2020-12-14 15:31:30 +00:00
|
|
|
static int
|
|
|
|
nvme_tcp_ctrlr_connect_qpair(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
struct nvme_tcp_qpair *tqpair;
|
2021-07-19 08:57:48 +00:00
|
|
|
struct nvme_tcp_poll_group *tgroup;
|
2020-12-14 15:31:30 +00:00
|
|
|
|
|
|
|
tqpair = nvme_tcp_qpair(qpair);
|
|
|
|
|
|
|
|
if (!tqpair->sock) {
|
|
|
|
rc = nvme_tcp_qpair_connect_sock(ctrlr, qpair);
|
|
|
|
if (rc < 0) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-01 08:37:36 +00:00
|
|
|
if (qpair->poll_group) {
|
|
|
|
rc = nvme_poll_group_connect_qpair(qpair);
|
|
|
|
if (rc) {
|
|
|
|
SPDK_ERRLOG("Unable to activate the tcp qpair.\n");
|
|
|
|
return rc;
|
|
|
|
}
|
2021-07-19 08:57:48 +00:00
|
|
|
tgroup = nvme_tcp_poll_group(qpair->poll_group);
|
|
|
|
tqpair->stats = &tgroup->stats;
|
2021-12-21 01:33:37 +00:00
|
|
|
tqpair->shared_stats = true;
|
2021-07-19 08:57:48 +00:00
|
|
|
} else {
|
|
|
|
tqpair->stats = calloc(1, sizeof(*tqpair->stats));
|
|
|
|
if (!tqpair->stats) {
|
|
|
|
SPDK_ERRLOG("tcp stats memory allocation failed\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2020-09-01 08:37:36 +00:00
|
|
|
}
|
|
|
|
|
2019-05-27 17:18:51 +00:00
|
|
|
tqpair->maxr2t = NVME_TCP_MAX_R2T_DEFAULT;
|
2019-06-04 12:56:45 +00:00
|
|
|
/* Explicitly set the state and recv_state of tqpair */
|
|
|
|
tqpair->state = NVME_TCP_QPAIR_STATE_INVALID;
|
|
|
|
if (tqpair->recv_state != NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_READY) {
|
|
|
|
nvme_tcp_qpair_set_recv_state(tqpair, NVME_TCP_PDU_RECV_STATE_AWAIT_PDU_READY);
|
|
|
|
}
|
2018-08-02 02:21:45 +00:00
|
|
|
rc = nvme_tcp_qpair_icreq_send(tqpair);
|
|
|
|
if (rc != 0) {
|
|
|
|
SPDK_ERRLOG("Unable to connect the tqpair\n");
|
2020-08-17 12:46:05 +00:00
|
|
|
return rc;
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
2021-05-28 16:42:48 +00:00
|
|
|
return rc;
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct spdk_nvme_qpair *
|
|
|
|
nvme_tcp_ctrlr_create_qpair(struct spdk_nvme_ctrlr *ctrlr,
|
|
|
|
uint16_t qid, uint32_t qsize,
|
|
|
|
enum spdk_nvme_qprio qprio,
|
2021-08-03 12:42:47 +00:00
|
|
|
uint32_t num_requests, bool async)
|
2018-08-02 02:21:45 +00:00
|
|
|
{
|
|
|
|
struct nvme_tcp_qpair *tqpair;
|
|
|
|
struct spdk_nvme_qpair *qpair;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
tqpair = calloc(1, sizeof(struct nvme_tcp_qpair));
|
|
|
|
if (!tqpair) {
|
|
|
|
SPDK_ERRLOG("failed to get create tqpair\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
tqpair->num_entries = qsize;
|
|
|
|
qpair = &tqpair->qpair;
|
2021-08-03 12:42:47 +00:00
|
|
|
rc = nvme_qpair_init(qpair, qid, ctrlr, qprio, num_requests, async);
|
2018-08-02 02:21:45 +00:00
|
|
|
if (rc != 0) {
|
2018-11-20 14:23:07 +00:00
|
|
|
free(tqpair);
|
2018-08-02 02:21:45 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-05-06 22:18:49 +00:00
|
|
|
rc = nvme_tcp_alloc_reqs(tqpair);
|
|
|
|
if (rc) {
|
2019-12-27 00:20:07 +00:00
|
|
|
nvme_tcp_ctrlr_delete_io_qpair(ctrlr, qpair);
|
2019-05-06 22:18:49 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-12-14 15:31:30 +00:00
|
|
|
/* spdk_nvme_qpair_get_optimal_poll_group needs socket information.
|
|
|
|
* So create the socket first when creating a qpair. */
|
|
|
|
rc = nvme_tcp_qpair_connect_sock(ctrlr, qpair);
|
|
|
|
if (rc) {
|
|
|
|
nvme_tcp_ctrlr_delete_io_qpair(ctrlr, qpair);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-08-02 02:21:45 +00:00
|
|
|
return qpair;
|
|
|
|
}
|
|
|
|
|
2020-02-10 19:13:06 +00:00
|
|
|
static struct spdk_nvme_qpair *
|
2018-08-02 02:21:45 +00:00
|
|
|
nvme_tcp_ctrlr_create_io_qpair(struct spdk_nvme_ctrlr *ctrlr, uint16_t qid,
|
|
|
|
const struct spdk_nvme_io_qpair_opts *opts)
|
|
|
|
{
|
|
|
|
return nvme_tcp_ctrlr_create_qpair(ctrlr, qid, opts->io_queue_size, opts->qprio,
|
2021-08-03 12:42:47 +00:00
|
|
|
opts->io_queue_requests, opts->async_mode);
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
2020-02-10 19:13:06 +00:00
|
|
|
static struct spdk_nvme_ctrlr *nvme_tcp_ctrlr_construct(const struct spdk_nvme_transport_id *trid,
|
2018-08-02 02:21:45 +00:00
|
|
|
const struct spdk_nvme_ctrlr_opts *opts,
|
|
|
|
void *devhandle)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_ctrlr *tctrlr;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
tctrlr = calloc(1, sizeof(*tctrlr));
|
|
|
|
if (tctrlr == NULL) {
|
|
|
|
SPDK_ERRLOG("could not allocate ctrlr\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
tctrlr->ctrlr.opts = *opts;
|
|
|
|
tctrlr->ctrlr.trid = *trid;
|
|
|
|
|
|
|
|
rc = nvme_ctrlr_construct(&tctrlr->ctrlr);
|
|
|
|
if (rc != 0) {
|
2018-12-09 15:55:54 +00:00
|
|
|
free(tctrlr);
|
2018-08-02 02:21:45 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
tctrlr->ctrlr.adminq = nvme_tcp_ctrlr_create_qpair(&tctrlr->ctrlr, 0,
|
2020-03-03 15:27:30 +00:00
|
|
|
tctrlr->ctrlr.opts.admin_queue_size, 0,
|
2021-08-03 12:42:47 +00:00
|
|
|
tctrlr->ctrlr.opts.admin_queue_size, true);
|
2018-08-02 02:21:45 +00:00
|
|
|
if (!tctrlr->ctrlr.adminq) {
|
|
|
|
SPDK_ERRLOG("failed to create admin qpair\n");
|
2018-12-09 15:55:54 +00:00
|
|
|
nvme_tcp_ctrlr_destruct(&tctrlr->ctrlr);
|
2018-08-02 02:21:45 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-02-18 20:16:24 +00:00
|
|
|
if (nvme_ctrlr_add_process(&tctrlr->ctrlr, 0) != 0) {
|
|
|
|
SPDK_ERRLOG("nvme_ctrlr_add_process() failed\n");
|
|
|
|
nvme_ctrlr_destruct(&tctrlr->ctrlr);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-08-02 02:21:45 +00:00
|
|
|
return &tctrlr->ctrlr;
|
|
|
|
}
|
|
|
|
|
2020-02-10 19:13:06 +00:00
|
|
|
static uint32_t
|
2018-08-02 02:21:45 +00:00
|
|
|
nvme_tcp_ctrlr_get_max_xfer_size(struct spdk_nvme_ctrlr *ctrlr)
|
|
|
|
{
|
2021-11-25 01:40:58 +00:00
|
|
|
/* TCP transport doesn't limit maximum IO transfer size. */
|
2019-06-27 06:15:56 +00:00
|
|
|
return UINT32_MAX;
|
2018-08-02 02:21:45 +00:00
|
|
|
}
|
|
|
|
|
2020-02-10 19:13:06 +00:00
|
|
|
static uint16_t
|
2018-08-02 02:21:45 +00:00
|
|
|
nvme_tcp_ctrlr_get_max_sges(struct spdk_nvme_ctrlr *ctrlr)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We do not support >1 SGE in the initiator currently,
|
|
|
|
* so we can only return 1 here. Once that support is
|
|
|
|
* added, this should return ctrlr->cdata.nvmf_specific.msdbd
|
|
|
|
* instead.
|
|
|
|
*/
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-05-17 22:05:51 +00:00
|
|
|
static int
|
|
|
|
nvme_tcp_qpair_iterate_requests(struct spdk_nvme_qpair *qpair,
|
|
|
|
int (*iter_fn)(struct nvme_request *req, void *arg),
|
|
|
|
void *arg)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_qpair *tqpair = nvme_tcp_qpair(qpair);
|
|
|
|
struct nvme_tcp_req *tcp_req, *tmp;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
assert(iter_fn != NULL);
|
|
|
|
|
|
|
|
TAILQ_FOREACH_SAFE(tcp_req, &tqpair->outstanding_reqs, link, tmp) {
|
|
|
|
assert(tcp_req->req != NULL);
|
|
|
|
|
|
|
|
rc = iter_fn(tcp_req->req, arg);
|
|
|
|
if (rc != 0) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-02-10 19:13:06 +00:00
|
|
|
static void
|
2019-05-06 22:29:24 +00:00
|
|
|
nvme_tcp_admin_qpair_abort_aers(struct spdk_nvme_qpair *qpair)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_req *tcp_req, *tmp;
|
|
|
|
struct spdk_nvme_cpl cpl;
|
|
|
|
struct nvme_tcp_qpair *tqpair = nvme_tcp_qpair(qpair);
|
|
|
|
|
|
|
|
cpl.status.sc = SPDK_NVME_SC_ABORTED_SQ_DELETION;
|
|
|
|
cpl.status.sct = SPDK_NVME_SCT_GENERIC;
|
|
|
|
|
|
|
|
TAILQ_FOREACH_SAFE(tcp_req, &tqpair->outstanding_reqs, link, tmp) {
|
|
|
|
assert(tcp_req->req != NULL);
|
2020-06-30 16:28:27 +00:00
|
|
|
if (tcp_req->req->cmd.opc != SPDK_NVME_OPC_ASYNC_EVENT_REQUEST) {
|
2019-07-18 11:36:02 +00:00
|
|
|
continue;
|
|
|
|
}
|
2019-05-06 22:29:24 +00:00
|
|
|
|
2020-06-30 16:28:27 +00:00
|
|
|
nvme_tcp_req_complete(tcp_req, &cpl);
|
2019-05-06 22:29:24 +00:00
|
|
|
nvme_tcp_req_put(tqpair, tcp_req);
|
|
|
|
}
|
|
|
|
}
|
2019-12-26 17:10:02 +00:00
|
|
|
|
2020-02-05 20:25:05 +00:00
|
|
|
static struct spdk_nvme_transport_poll_group *
|
|
|
|
nvme_tcp_poll_group_create(void)
|
|
|
|
{
|
2020-02-06 19:43:31 +00:00
|
|
|
struct nvme_tcp_poll_group *group = calloc(1, sizeof(*group));
|
|
|
|
|
|
|
|
if (group == NULL) {
|
|
|
|
SPDK_ERRLOG("Unable to allocate poll group.\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-03-05 21:00:12 +00:00
|
|
|
TAILQ_INIT(&group->needs_poll);
|
|
|
|
|
2020-02-13 21:52:59 +00:00
|
|
|
group->sock_group = spdk_sock_group_create(group);
|
|
|
|
if (group->sock_group == NULL) {
|
|
|
|
free(group);
|
|
|
|
SPDK_ERRLOG("Unable to allocate sock group.\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-02-06 19:43:31 +00:00
|
|
|
return &group->group;
|
2020-02-05 20:25:05 +00:00
|
|
|
}
|
|
|
|
|
2020-07-13 15:48:29 +00:00
|
|
|
static struct spdk_nvme_transport_poll_group *
|
|
|
|
nvme_tcp_qpair_get_optimal_poll_group(struct spdk_nvme_qpair *qpair)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_qpair *tqpair = nvme_tcp_qpair(qpair);
|
|
|
|
struct spdk_sock_group *group = NULL;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = spdk_sock_get_optimal_sock_group(tqpair->sock, &group);
|
|
|
|
if (!rc && group != NULL) {
|
|
|
|
return spdk_sock_group_get_ctx(group);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-02-05 20:25:05 +00:00
|
|
|
static int
|
2020-04-07 17:20:41 +00:00
|
|
|
nvme_tcp_poll_group_connect_qpair(struct spdk_nvme_qpair *qpair)
|
2020-02-05 20:25:05 +00:00
|
|
|
{
|
2020-02-13 21:52:59 +00:00
|
|
|
struct nvme_tcp_poll_group *group = nvme_tcp_poll_group(qpair->poll_group);
|
|
|
|
struct nvme_tcp_qpair *tqpair = nvme_tcp_qpair(qpair);
|
|
|
|
|
|
|
|
if (spdk_sock_group_add_sock(group->sock_group, tqpair->sock, nvme_tcp_qpair_sock_cb, qpair)) {
|
|
|
|
return -EPROTO;
|
|
|
|
}
|
2020-02-05 20:25:05 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2020-04-07 17:20:41 +00:00
|
|
|
nvme_tcp_poll_group_disconnect_qpair(struct spdk_nvme_qpair *qpair)
|
2020-02-05 20:25:05 +00:00
|
|
|
{
|
2020-02-13 21:52:59 +00:00
|
|
|
struct nvme_tcp_poll_group *group = nvme_tcp_poll_group(qpair->poll_group);
|
|
|
|
struct nvme_tcp_qpair *tqpair = nvme_tcp_qpair(qpair);
|
|
|
|
|
2021-03-05 21:00:12 +00:00
|
|
|
if (tqpair->needs_poll) {
|
|
|
|
TAILQ_REMOVE(&group->needs_poll, tqpair, link);
|
|
|
|
tqpair->needs_poll = false;
|
|
|
|
}
|
|
|
|
|
2020-02-13 21:52:59 +00:00
|
|
|
if (tqpair->sock && group->sock_group) {
|
|
|
|
if (spdk_sock_group_remove_sock(group->sock_group, tqpair->sock)) {
|
|
|
|
return -EPROTO;
|
|
|
|
}
|
|
|
|
}
|
2020-02-05 20:25:05 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
nvme_tcp_poll_group_add(struct spdk_nvme_transport_poll_group *tgroup,
|
|
|
|
struct spdk_nvme_qpair *qpair)
|
|
|
|
{
|
2020-02-13 21:52:59 +00:00
|
|
|
struct nvme_tcp_qpair *tqpair = nvme_tcp_qpair(qpair);
|
|
|
|
struct nvme_tcp_poll_group *group = nvme_tcp_poll_group(tgroup);
|
|
|
|
|
|
|
|
/* disconnected qpairs won't have a sock to add. */
|
|
|
|
if (nvme_qpair_get_state(qpair) >= NVME_QPAIR_CONNECTED) {
|
|
|
|
if (spdk_sock_group_add_sock(group->sock_group, tqpair->sock, nvme_tcp_qpair_sock_cb, qpair)) {
|
|
|
|
return -EPROTO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-06 19:43:31 +00:00
|
|
|
return 0;
|
2020-02-05 20:25:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
nvme_tcp_poll_group_remove(struct spdk_nvme_transport_poll_group *tgroup,
|
|
|
|
struct spdk_nvme_qpair *qpair)
|
|
|
|
{
|
2021-07-19 08:57:48 +00:00
|
|
|
struct nvme_tcp_qpair *tqpair;
|
|
|
|
|
2021-12-23 15:24:16 +00:00
|
|
|
assert(qpair->poll_group_tailq_head == &tgroup->disconnected_qpairs);
|
2020-02-13 21:52:59 +00:00
|
|
|
|
2021-07-19 08:57:48 +00:00
|
|
|
tqpair = nvme_tcp_qpair(qpair);
|
2021-12-21 01:33:37 +00:00
|
|
|
|
|
|
|
assert(tqpair->shared_stats == true);
|
|
|
|
tqpair->stats = &g_dummy_stats;
|
2021-07-19 08:57:48 +00:00
|
|
|
|
2021-12-23 15:24:16 +00:00
|
|
|
return 0;
|
2020-02-05 20:25:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int64_t
|
|
|
|
nvme_tcp_poll_group_process_completions(struct spdk_nvme_transport_poll_group *tgroup,
|
2020-04-07 17:20:41 +00:00
|
|
|
uint32_t completions_per_qpair, spdk_nvme_disconnected_qpair_cb disconnected_qpair_cb)
|
2020-02-05 20:25:05 +00:00
|
|
|
{
|
2020-02-13 21:52:59 +00:00
|
|
|
struct nvme_tcp_poll_group *group = nvme_tcp_poll_group(tgroup);
|
2020-02-06 19:43:31 +00:00
|
|
|
struct spdk_nvme_qpair *qpair, *tmp_qpair;
|
2021-03-05 21:00:12 +00:00
|
|
|
struct nvme_tcp_qpair *tqpair, *tmp_tqpair;
|
2021-08-24 11:52:28 +00:00
|
|
|
int num_events;
|
2020-02-13 21:52:59 +00:00
|
|
|
|
|
|
|
group->completions_per_qpair = completions_per_qpair;
|
|
|
|
group->num_completions = 0;
|
2021-07-19 08:57:48 +00:00
|
|
|
group->stats.polls++;
|
2020-02-13 21:52:59 +00:00
|
|
|
|
2021-08-24 11:52:28 +00:00
|
|
|
num_events = spdk_sock_group_poll(group->sock_group);
|
2020-02-06 19:43:31 +00:00
|
|
|
|
|
|
|
STAILQ_FOREACH_SAFE(qpair, &tgroup->disconnected_qpairs, poll_group_stailq, tmp_qpair) {
|
|
|
|
disconnected_qpair_cb(qpair, tgroup->group->ctx);
|
|
|
|
}
|
|
|
|
|
2021-03-05 21:00:12 +00:00
|
|
|
/* If any qpairs were marked as needing to be polled due to an asynchronous write completion
|
|
|
|
* and they weren't polled as a consequence of calling spdk_sock_group_poll above, poll them now. */
|
|
|
|
TAILQ_FOREACH_SAFE(tqpair, &group->needs_poll, link, tmp_tqpair) {
|
|
|
|
nvme_tcp_qpair_sock_cb(&tqpair->qpair, group->sock_group, tqpair->sock);
|
|
|
|
}
|
|
|
|
|
2021-08-24 11:52:28 +00:00
|
|
|
if (spdk_unlikely(num_events < 0)) {
|
|
|
|
return num_events;
|
|
|
|
}
|
|
|
|
|
2021-07-19 08:57:48 +00:00
|
|
|
group->stats.idle_polls += !num_events;
|
|
|
|
group->stats.socket_completions += num_events;
|
|
|
|
|
2020-02-13 21:52:59 +00:00
|
|
|
return group->num_completions;
|
2020-02-05 20:25:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
nvme_tcp_poll_group_destroy(struct spdk_nvme_transport_poll_group *tgroup)
|
|
|
|
{
|
2020-02-13 21:52:59 +00:00
|
|
|
int rc;
|
|
|
|
struct nvme_tcp_poll_group *group = nvme_tcp_poll_group(tgroup);
|
|
|
|
|
2020-02-06 19:43:31 +00:00
|
|
|
if (!STAILQ_EMPTY(&tgroup->connected_qpairs) || !STAILQ_EMPTY(&tgroup->disconnected_qpairs)) {
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
2020-02-13 21:52:59 +00:00
|
|
|
rc = spdk_sock_group_close(&group->sock_group);
|
|
|
|
if (rc != 0) {
|
|
|
|
SPDK_ERRLOG("Failed to close the sock group for a tcp poll group.\n");
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
|
2020-02-06 19:43:31 +00:00
|
|
|
free(tgroup);
|
|
|
|
|
|
|
|
return 0;
|
2020-02-05 20:25:05 +00:00
|
|
|
}
|
|
|
|
|
2021-07-19 08:57:48 +00:00
|
|
|
static int
|
|
|
|
nvme_tcp_poll_group_get_stats(struct spdk_nvme_transport_poll_group *tgroup,
|
|
|
|
struct spdk_nvme_transport_poll_group_stat **_stats)
|
|
|
|
{
|
|
|
|
struct nvme_tcp_poll_group *group;
|
|
|
|
struct spdk_nvme_transport_poll_group_stat *stats;
|
|
|
|
|
|
|
|
if (tgroup == NULL || _stats == NULL) {
|
|
|
|
SPDK_ERRLOG("Invalid stats or group pointer\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
group = nvme_tcp_poll_group(tgroup);
|
|
|
|
|
|
|
|
stats = calloc(1, sizeof(*stats));
|
|
|
|
if (!stats) {
|
|
|
|
SPDK_ERRLOG("Can't allocate memory for TCP stats\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
stats->trtype = SPDK_NVME_TRANSPORT_TCP;
|
|
|
|
memcpy(&stats->tcp, &group->stats, sizeof(group->stats));
|
|
|
|
|
|
|
|
*_stats = stats;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nvme_tcp_poll_group_free_stats(struct spdk_nvme_transport_poll_group *tgroup,
|
|
|
|
struct spdk_nvme_transport_poll_group_stat *stats)
|
|
|
|
{
|
|
|
|
free(stats);
|
|
|
|
}
|
|
|
|
|
2019-12-26 17:10:02 +00:00
|
|
|
const struct spdk_nvme_transport_ops tcp_ops = {
|
|
|
|
.name = "TCP",
|
|
|
|
.type = SPDK_NVME_TRANSPORT_TCP,
|
|
|
|
.ctrlr_construct = nvme_tcp_ctrlr_construct,
|
|
|
|
.ctrlr_scan = nvme_fabric_ctrlr_scan,
|
|
|
|
.ctrlr_destruct = nvme_tcp_ctrlr_destruct,
|
|
|
|
.ctrlr_enable = nvme_tcp_ctrlr_enable,
|
|
|
|
|
|
|
|
.ctrlr_set_reg_4 = nvme_fabric_ctrlr_set_reg_4,
|
|
|
|
.ctrlr_set_reg_8 = nvme_fabric_ctrlr_set_reg_8,
|
|
|
|
.ctrlr_get_reg_4 = nvme_fabric_ctrlr_get_reg_4,
|
|
|
|
.ctrlr_get_reg_8 = nvme_fabric_ctrlr_get_reg_8,
|
2021-06-23 08:50:16 +00:00
|
|
|
.ctrlr_set_reg_4_async = nvme_fabric_ctrlr_set_reg_4_async,
|
|
|
|
.ctrlr_set_reg_8_async = nvme_fabric_ctrlr_set_reg_8_async,
|
|
|
|
.ctrlr_get_reg_4_async = nvme_fabric_ctrlr_get_reg_4_async,
|
|
|
|
.ctrlr_get_reg_8_async = nvme_fabric_ctrlr_get_reg_8_async,
|
2019-12-26 17:10:02 +00:00
|
|
|
|
|
|
|
.ctrlr_get_max_xfer_size = nvme_tcp_ctrlr_get_max_xfer_size,
|
|
|
|
.ctrlr_get_max_sges = nvme_tcp_ctrlr_get_max_sges,
|
|
|
|
|
|
|
|
.ctrlr_create_io_qpair = nvme_tcp_ctrlr_create_io_qpair,
|
|
|
|
.ctrlr_delete_io_qpair = nvme_tcp_ctrlr_delete_io_qpair,
|
|
|
|
.ctrlr_connect_qpair = nvme_tcp_ctrlr_connect_qpair,
|
|
|
|
.ctrlr_disconnect_qpair = nvme_tcp_ctrlr_disconnect_qpair,
|
|
|
|
|
|
|
|
.qpair_abort_reqs = nvme_tcp_qpair_abort_reqs,
|
|
|
|
.qpair_reset = nvme_tcp_qpair_reset,
|
|
|
|
.qpair_submit_request = nvme_tcp_qpair_submit_request,
|
|
|
|
.qpair_process_completions = nvme_tcp_qpair_process_completions,
|
2020-05-17 22:05:51 +00:00
|
|
|
.qpair_iterate_requests = nvme_tcp_qpair_iterate_requests,
|
2019-12-26 17:10:02 +00:00
|
|
|
.admin_qpair_abort_aers = nvme_tcp_admin_qpair_abort_aers,
|
2020-02-05 20:25:05 +00:00
|
|
|
|
|
|
|
.poll_group_create = nvme_tcp_poll_group_create,
|
2020-07-13 15:48:29 +00:00
|
|
|
.qpair_get_optimal_poll_group = nvme_tcp_qpair_get_optimal_poll_group,
|
2020-04-07 17:20:41 +00:00
|
|
|
.poll_group_connect_qpair = nvme_tcp_poll_group_connect_qpair,
|
|
|
|
.poll_group_disconnect_qpair = nvme_tcp_poll_group_disconnect_qpair,
|
2020-02-05 20:25:05 +00:00
|
|
|
.poll_group_add = nvme_tcp_poll_group_add,
|
|
|
|
.poll_group_remove = nvme_tcp_poll_group_remove,
|
|
|
|
.poll_group_process_completions = nvme_tcp_poll_group_process_completions,
|
|
|
|
.poll_group_destroy = nvme_tcp_poll_group_destroy,
|
2021-07-19 08:57:48 +00:00
|
|
|
.poll_group_get_stats = nvme_tcp_poll_group_get_stats,
|
|
|
|
.poll_group_free_stats = nvme_tcp_poll_group_free_stats,
|
2019-12-26 17:10:02 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
SPDK_NVME_TRANSPORT_REGISTER(tcp, &tcp_ops);
|