@@ -69,6 +69,7 @@ enum nvme_tcp_queue_flags {
NVME_TCP_Q_LIVE = 1,
NVME_TCP_Q_POLLING = 2,
NVME_TCP_Q_OFF_DDP = 3,
+ NVME_TCP_Q_OFF_DDGST_RX = 4,
};
enum nvme_tcp_recv_state {
@@ -96,6 +97,7 @@ struct nvme_tcp_queue {
size_t data_remaining;
size_t ddgst_remaining;
unsigned int nr_cqe;
+ bool ddgst_valid;
/* send state */
struct nvme_tcp_request *request;
@@ -230,6 +232,22 @@ static inline size_t nvme_tcp_pdu_last_send(struct nvme_tcp_request *req,
return nvme_tcp_pdu_data_left(req) <= len;
}
+static inline bool nvme_tcp_ddp_ddgst_ok(struct nvme_tcp_queue *queue)
+{
+ return queue->ddgst_valid;
+}
+
+static inline void nvme_tcp_ddp_ddgst_update(struct nvme_tcp_queue *queue,
+ struct sk_buff *skb)
+{
+ if (queue->ddgst_valid)
+#ifdef CONFIG_ULP_DDP
+ queue->ddgst_valid = skb->ddp_crc;
+#else
+ queue->ddgst_valid = false;
+#endif
+}
+
static int nvme_tcp_req_map_sg(struct nvme_tcp_request *req, struct request *rq)
{
int ret;
@@ -243,6 +261,26 @@ static int nvme_tcp_req_map_sg(struct nvme_tcp_request *req, struct request *rq)
return 0;
}
+static void nvme_tcp_ddp_ddgst_recalc(struct ahash_request *hash,
+ struct request *rq)
+{
+ struct nvme_tcp_request *req;
+
+ if (!rq)
+ return;
+
+ req = blk_mq_rq_to_pdu(rq);
+
+ if (!req->offloaded && nvme_tcp_req_map_sg(req, rq))
+ return;
+
+ crypto_ahash_init(hash);
+ req->ddp.sg_table.sgl = req->ddp.first_sgl;
+ ahash_request_set_crypt(hash, req->ddp.sg_table.sgl, NULL,
+ le32_to_cpu(req->data_len));
+ crypto_ahash_update(hash);
+}
+
#ifdef CONFIG_ULP_DDP
static bool nvme_tcp_resync_request(struct sock *sk, u32 seq, u32 flags);
@@ -330,8 +368,10 @@ static int nvme_tcp_offload_socket(struct nvme_tcp_queue *queue)
}
inet_csk(queue->sock->sk)->icsk_ulp_ddp_ops = &nvme_tcp_ddp_ulp_ops;
- if (netdev->features & NETIF_F_HW_ULP_DDP)
+ if (netdev->features & NETIF_F_HW_ULP_DDP) {
set_bit(NVME_TCP_Q_OFF_DDP, &queue->flags);
+ set_bit(NVME_TCP_Q_OFF_DDGST_RX, &queue->flags);
+ }
return ret;
}
@@ -346,6 +386,7 @@ static void nvme_tcp_unoffload_socket(struct nvme_tcp_queue *queue)
}
clear_bit(NVME_TCP_Q_OFF_DDP, &queue->flags);
+ clear_bit(NVME_TCP_Q_OFF_DDGST_RX, &queue->flags);
netdev->ulp_ddp_ops->ulp_ddp_sk_del(netdev, queue->sock->sk);
@@ -721,6 +762,7 @@ static void nvme_tcp_init_recv_ctx(struct nvme_tcp_queue *queue)
queue->pdu_offset = 0;
queue->data_remaining = -1;
queue->ddgst_remaining = 0;
+ queue->ddgst_valid = true;
}
static void nvme_tcp_error_recovery(struct nvme_ctrl *ctrl)
@@ -914,7 +956,8 @@ static int nvme_tcp_recv_pdu(struct nvme_tcp_queue *queue, struct sk_buff *skb,
size_t rcv_len = min_t(size_t, *len, queue->pdu_remaining);
int ret;
- if (test_bit(NVME_TCP_Q_OFF_DDP, &queue->flags))
+ if (test_bit(NVME_TCP_Q_OFF_DDP, &queue->flags) ||
+ test_bit(NVME_TCP_Q_OFF_DDGST_RX, &queue->flags))
nvme_tcp_resync_response(queue, skb, *offset);
ret = skb_copy_bits(skb, *offset,
@@ -977,14 +1020,14 @@ static int nvme_tcp_consume_skb(struct nvme_tcp_queue *queue, struct sk_buff *sk
#ifdef CONFIG_ULP_DDP
if (test_bit(NVME_TCP_Q_OFF_DDP, &queue->flags)) {
- if (queue->data_digest)
+ if (queue->data_digest && !test_bit(NVME_TCP_Q_OFF_DDGST_RX, &queue->flags))
ret = skb_ddp_copy_and_hash_datagram_iter(skb, *offset, iter, recv_len,
queue->rcv_hash);
else
ret = skb_ddp_copy_datagram_iter(skb, *offset, iter, recv_len);
} else {
#endif
- if (queue->data_digest)
+ if (queue->data_digest && !test_bit(NVME_TCP_Q_OFF_DDGST_RX, &queue->flags))
ret = skb_copy_and_hash_datagram_iter(skb, *offset, iter, recv_len,
queue->rcv_hash);
else
@@ -1003,6 +1046,8 @@ static int nvme_tcp_recv_data(struct nvme_tcp_queue *queue, struct sk_buff *skb,
struct nvme_tcp_request *req;
struct request *rq;
+ if (queue->data_digest && test_bit(NVME_TCP_Q_OFF_DDGST_RX, &queue->flags))
+ nvme_tcp_ddp_ddgst_update(queue, skb);
rq = blk_mq_tag_to_rq(nvme_tcp_tagset(queue), pdu->command_id);
if (!rq) {
dev_err(queue->ctrl->ctrl.device,
@@ -1055,7 +1100,6 @@ static int nvme_tcp_recv_data(struct nvme_tcp_queue *queue, struct sk_buff *skb,
if (!queue->data_remaining) {
if (queue->data_digest) {
- nvme_tcp_ddgst_final(queue->rcv_hash, &queue->exp_ddgst);
queue->ddgst_remaining = NVME_TCP_DIGEST_LENGTH;
} else {
if (pdu->hdr.flags & NVME_TCP_F_DATA_SUCCESS) {
@@ -1076,8 +1120,12 @@ static int nvme_tcp_recv_ddgst(struct nvme_tcp_queue *queue,
char *ddgst = (char *)&queue->recv_ddgst;
size_t recv_len = min_t(size_t, *len, queue->ddgst_remaining);
off_t off = NVME_TCP_DIGEST_LENGTH - queue->ddgst_remaining;
+ bool offload_fail, offload_en;
+ struct request *rq = NULL;
int ret;
+ if (test_bit(NVME_TCP_Q_OFF_DDGST_RX, &queue->flags))
+ nvme_tcp_ddp_ddgst_update(queue, skb);
ret = skb_copy_bits(skb, *offset, &ddgst[off], recv_len);
if (unlikely(ret))
return ret;
@@ -1088,18 +1136,25 @@ static int nvme_tcp_recv_ddgst(struct nvme_tcp_queue *queue,
if (queue->ddgst_remaining)
return 0;
- if (queue->recv_ddgst != queue->exp_ddgst) {
- dev_err(queue->ctrl->ctrl.device,
- "data digest error: recv %#x expected %#x\n",
- le32_to_cpu(queue->recv_ddgst),
- le32_to_cpu(queue->exp_ddgst));
- return -EIO;
+ rq = blk_mq_tag_to_rq(nvme_tcp_tagset(queue), pdu->command_id);
+
+ offload_fail = !nvme_tcp_ddp_ddgst_ok(queue);
+ offload_en = test_bit(NVME_TCP_Q_OFF_DDGST_RX, &queue->flags);
+ if (!offload_en || offload_fail) {
+ if (offload_en && offload_fail) // software-fallback
+ nvme_tcp_ddp_ddgst_recalc(queue->rcv_hash, rq);
+
+ nvme_tcp_ddgst_final(queue->rcv_hash, &queue->exp_ddgst);
+ if (queue->recv_ddgst != queue->exp_ddgst) {
+ dev_err(queue->ctrl->ctrl.device,
+ "data digest error: recv %#x expected %#x\n",
+ le32_to_cpu(queue->recv_ddgst),
+ le32_to_cpu(queue->exp_ddgst));
+ return -EIO;
+ }
}
if (pdu->hdr.flags & NVME_TCP_F_DATA_SUCCESS) {
- struct request *rq = blk_mq_tag_to_rq(nvme_tcp_tagset(queue),
- pdu->command_id);
-
nvme_tcp_end_request(rq, NVME_SC_SUCCESS);
queue->nr_cqe++;
}
@@ -1851,7 +1906,8 @@ static void __nvme_tcp_stop_queue(struct nvme_tcp_queue *queue)
nvme_tcp_restore_sock_calls(queue);
cancel_work_sync(&queue->io_work);
- if (test_bit(NVME_TCP_Q_OFF_DDP, &queue->flags))
+ if (test_bit(NVME_TCP_Q_OFF_DDP, &queue->flags) ||
+ test_bit(NVME_TCP_Q_OFF_DDGST_RX, &queue->flags))
nvme_tcp_unoffload_socket(queue);
}