@@ -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;
@@ -234,6 +236,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_TCP_DDP_CRC
+ 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;
@@ -247,7 +265,27 @@ static int nvme_tcp_req_map_sg(struct nvme_tcp_request *req, struct request *rq)
return 0;
}
-#ifdef CONFIG_TCP_DDP
+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);
+}
+
+#if defined(CONFIG_TCP_DDP) || defined(CONFIG_TCP_DDP_CRC)
static bool nvme_tcp_resync_request(struct sock *sk, u32 seq, u32 flags);
static void nvme_tcp_ddp_teardown_done(void *ddp_ctx);
@@ -316,7 +354,7 @@ static int nvme_tcp_offload_socket(struct nvme_tcp_queue *queue)
struct nvme_tcp_ddp_config config = {};
int ret;
- if (!(netdev->features & NETIF_F_HW_TCP_DDP))
+ if (!(netdev->features & (NETIF_F_HW_TCP_DDP | NETIF_F_HW_TCP_DDP_CRC_RX)))
return -EOPNOTSUPP;
config.cfg.type = TCP_DDP_NVME;
@@ -343,6 +381,9 @@ static int nvme_tcp_offload_socket(struct nvme_tcp_queue *queue)
if (netdev->features & NETIF_F_HW_TCP_DDP)
set_bit(NVME_TCP_Q_OFF_DDP, &queue->flags);
+ if (netdev->features & NETIF_F_HW_TCP_DDP_CRC_RX)
+ set_bit(NVME_TCP_Q_OFF_DDGST_RX, &queue->flags);
+
return ret;
}
@@ -373,7 +414,7 @@ static int nvme_tcp_offload_limits(struct nvme_tcp_queue *queue)
return -ENODEV;
}
- if (netdev->features & NETIF_F_HW_TCP_DDP &&
+ if ((netdev->features & (NETIF_F_HW_TCP_DDP | NETIF_F_HW_TCP_DDP_CRC_RX)) &&
netdev->tcp_ddp_ops &&
netdev->tcp_ddp_ops->tcp_ddp_limits)
ret = netdev->tcp_ddp_ops->tcp_ddp_limits(netdev, &limits);
@@ -720,6 +761,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)
@@ -906,7 +948,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,
@@ -995,6 +1038,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,
@@ -1047,7 +1092,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) {
@@ -1068,8 +1112,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;
@@ -1080,18 +1128,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++;
}
@@ -1833,7 +1888,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);
}